#0 @需求 #done 增加报表功能

#0 @需求 #done 增加报表功能

--- biz/issue.coffee
+++ biz/issue.coffee
@@ -7,6 +7,8 @@
 _AssetIssueRelation = require './asset_issue_relation'
 _async = require 'async'
 _common = require '../common'
+_Member = require './member'
+_moment = require 'moment'

 #定义一个Project类
 class Issue extends _BaseEntity
@@ -60,41 +62,47 @@
   find: (data, cb)->
     self = @
     cond = {}
-    cond.tag = data.tag
     cond.id = data.id
     cond.project_id = data.project_id

     #选项
     options =
       pagination: limit: data.limit, offset: data.offset
-      orderBy: timestamp: 'DESC'
+      orderBy: 'issue.status': 'desc', 'issue.timestamp': 'DESC'
       fields: (query)->
-        query.select query.knex.raw('*, (SELECT realname FROM member WHERE member.id = issue.owner) AS realname')
+        query.select ['issue.*', 'project.title AS project_name']
+        #query.select query.knex.raw('*, (SELECT realname FROM member WHERE member.id = issue.owner) AS realname')

       #在查询之前，对query再处理
       beforeQuery: (query)->
+        query.join('project', ()->
+          this.on 'issue.project_id', '=', 'project.id'
+        , 'left')
+
         query.limit data.limit || 10
         query.offset data.offset || 0
         #只取未完成的
         if(data.status is 'undone')
-          query.where 'status', '<>', 'done'
-          query.where 'status', '<>', 'trash'
+          query.where 'issue.status', '<>', 'done'
+          query.where 'issue.status', '<>', 'trash'
         else if data.status
-          query.where 'status', data.status
+          query.where 'issue.status', data.status
+        else
+          #默认是不获取trash的数据
+          query.where 'issue.status', '<>', 'trash'

         #指定标签
-        query.where 'tag', data.tag if data.tag
+        query.where 'issue.tag', data.tag if data.tag
+        #这里不查义project的tag
+        query.where 'issue.tag', '<>', 'project'
         #指定完成时间段
         #query.where 'finish_time', '>=', data.beginTime if data.beginTime
         #query.where 'finish_time', '<=', data.endTime if data.endTime
-        self.queryTimeRange query, 'finish_time', data.finish_time
-        self.queryTimeRange query, 'timestamp', data.timestamp
+        self.queryTimeRange query, 'issue.finish_time', data.finish_time
+        self.queryTimeRange query, 'issue.timestamp', data.timestamp

         #指定责任人
-        query.where 'owner', data.owner if data.owner
-
-        query.orderBy 'status', 'desc'
-        query.orderBy 'timestamp', 'desc'
+        query.where 'issue.owner', data.owner if data.owner isnt undefined

     super cond, options, cb
@@ -151,4 +159,72 @@

     #http://127.0.0.1:14318/api/project/1/discussion

+  #获取已关联的issue
+  findAssignedIssue: (start_time, end_time, data, cb)->
+    #http://localhost:8000/api/report/issue?start_time=1399790723523&end_time=1400136323523
+    self = @
+    index = 0
+
+
+    _async.whilst(
+      (-> return index < data.assigned.length)
+      ((done)->
+        member = data.assigned[index].member
+        sql = "SELECT A.*, B.title AS project_name FROM issue A LEFT JOIN project B ON A.project_id = B.id
+          WHERE A.owner = #{member.id} AND (
+            (A.finish_time BETWEEN #{start_time} AND #{end_time})
+              OR
+            (A.timestamp BETWEEN #{start_time} AND #{end_time})
+          ) AND A.status <> 'trash' AND A.tag <> 'project'"
+
+        self.entity().knex.raw(sql).exec (err, result)->
+          return done err if err
+          data.assigned[index].issue = result[0]
+          index++
+          done(err)
+      )
+      cb
+    )
+
+  #获取报表
+  report: (req, res, next)->
+    now = Number(new Date())
+    start_time = Number(req.query.start_time)
+    end_time = Number(req.query.end_time)
+    start_time = now if isNaN(start_time)
+    end_time = now if isNaN(end_time)
+
+    ###本周
+    if not start_time and not end_time
+      end = _moment()
+      start = _moment(end).subtract('days', end.day())
+      time_range = Number(start) + "|" + Number(end)
+    ###
+
+    result = assigned: [], unassigned: []
+    self = @
+    queue = []
+
+    #获取用户，并获取他们的owner
+    queue.push (
+      (done)->
+        member = new _Member self.member
+        member.find {}, (err, data)->
+          result.assigned.push member: member for member in data.items
+          self.findAssignedIssue start_time, end_time, result, done
+    )
+
+    #查询未关联到人的，即没有owner的任务
+    queue.push(
+      (done)->
+        self.find owner: null, (err, data)->
+          result.unassigned = data.items
+          done null
+    )
+
+    _async.series queue, (err)->
+      _common.response500 res, err if err
+      res.json result
+
+
 module.exports = Issue


Typo.

Typo.

--- server/group.coffee
+++ server/group.coffee
@@ -123,14 +123,13 @@

   keywords = (keyword.replace /[-\\^$*+?.()|[\]{}]/g, '\\$&' for keyword in query.split /\s+/)

-  # TODO: Except list does not really work (at least for groups, for persons it somehow works): https://jira.mongodb.org/browse/SERVER-13511
   findPersonQuery =
     $and: []
     _id:
       $nin: except
   findGroupQuery =
     $and: []
-    _in:
+    _id:
       $nin: except

   # TODO: Use some smarter searching with provided query, probably using some real full-text search instead of regex


Added header to Commander

Added header to Commander

--- source/Commander.coffee
+++ source/Commander.coffee
@@ -0,0 +1,4 @@
+###
+  Commander module for MP3 Downloader.
+  Written by Jense5.
+###


refactor: remove messy comment (#115)

refactor: remove messy comment (#115)

--- grammars/repositories/inlines/emphasis-grammar.cson
+++ grammars/repositories/inlines/emphasis-grammar.cson
@@ -9,7 +9,7 @@
   #   e__mphasis__ phrase
   #
   name: 'markup.emphasis.unconstrained.asciidoc'
-  match: '(?<!\\\\\\\\)(\\[(?:[^\\]]+?)\\])?((__)((?!_).+?)(__))' # now including the underscores in the match
+  match: '(?<!\\\\\\\\)(\\[(?:[^\\]]+?)\\])?((__)((?!_).+?)(__))'
   captures:
     1: name: 'markup.meta.attribute-list.asciidoc'
     2: name: 'markup.italic.asciidoc'


fix(participants): Remove all sorts of broken cruft, remove "Me"

--- internal_packages/inbox-participants/lib/InboxParticipants.cjsx
+++ internal_packages/inbox-participants/lib/InboxParticipants.cjsx
@@ -1,8 +1,6 @@
 {ComponentRegistry, NamespaceStore} = require "inbox-exports"
 React = require "react"
 _ = require "underscore-plus"
-
-SORT = false

 DefaultChip = React.createClass
   render: ->
@@ -35,51 +33,17 @@
     </div>

   getParticipants: ->
-    me = NamespaceStore.current().me()
+    myEmail = NamespaceStore.current().emailAddress
+    list = @props.participants

-    if SORT
-      sorted = _.sortBy @props.participants, (p) -> p.displayName()
-    else
-      sorted = @props.participants
+    # Remove 'Me' if there is more than one participant
+    if list.length > 1
+      list = _.reject list, (p) -> p.email is myEmail

-    # Pull 'me'
-    user = _.findWhere sorted, me
-    if self
-      sorted = _.without sorted, user
-
-    # Push 'me' if we just emptied the list
-    if user? and sorted.length == 0
-      sorted.push me
-    # Unshift 'me' if context says we're the sender
-    else if @_fromUserUnrepliedContext()
-      sorted.unshift me
-    # Unshift 'me' if context says we're the sender and it's a group thread
-    else if @_fromUserGroupThreadContext()
-      sorted.unshift me
-
-    # Pull the sender to the front, if it's not 'me'
-    if @_getSender()? and @_getSender().email != me.email
-      # It's probably the same contact object but why risk the fuck up?
-      sender = _.findWhere sorted, {email: @_getSender().email}
-      sorted = _.without sorted, sender
-      sorted.unshift sender
-
-    sorted.forEach (p) ->
+    list.forEach (p) ->
       p.id = p.name+p.email

-    sorted
+    list

   shouldComponentUpdate: (newProps, newState) ->
     !_.isEqual(newProps.participants, @props.participants)
-
-  # TODO - this'll require poking DatabaseStore
-  # UPDATE: Please move these so they're injected via props from somewhere
-  # that has the thread object. DON'T QUERY HERE! (@ben)
-  _fromUserUnrepliedContext: ->
-    false
-
-  _fromUserGroupThreadContext: ->
-    false
-
-  _getSender: ->
-    undefined


Improved error message for missing API/secret key

--- spec/client-spec.coffee
+++ spec/client-spec.coffee
@@ -5,8 +5,14 @@
 sys = require('sys')
 Client = beaconpush.Client

-API_KEY = 'REPLACE'
-SECRET_KEY = 'REPLACE'
+API_KEY = process.env['BEACONPUSH_API_KEY']
+SECRET_KEY = process.env['BEACONPUSH_SECRET_KEY']
+
+if not API_KEY? or not SECRET_KEY?
+  sys.puts 'Missing env. variable BEACONPUSH_API_KEY and/or BEACONPUSH_SECRET_KEY'
+  sys.puts 'They are required to run the tests. Look up your keys under the "Account" page on beaconpush.com'
+  sys.puts 'and set them accordingly'
+  process.exit 1

 vows.describe('Beaconpush REST API v1 Client').addBatch
   'A Client':


:memo: Correct spec description

:memo: Correct spec description

--- spec/editor-spec.coffee
+++ spec/editor-spec.coffee
@@ -3271,7 +3271,7 @@
       expect(editor.getCursorBufferPosition().row).toBe 0

   describe ".selectPageUp/Down()", ->
-    it "select one screen height of text up or down", ->
+    it "selects one screen height of text up or down", ->
       editor.manageScrollPosition = true

       editor.setLineHeightInPixels(10)


Added limit/skip.

Added limit/skip.

--- spec/finder-spec.coffee
+++ spec/finder-spec.coffee
@@ -454,7 +454,7 @@
               titles = (post.title for post in posts)
               assert.deepEqual titles, ["Test 3", "Test 2", "Test 1"]

-        "combined order":
+        "combined asc, desc":
           topic: (scope)->
             connect().find("posts")
           "same order":
@@ -470,6 +470,35 @@
               titles = (post.title for post in posts)
               assert.deepEqual titles, ["Test 2", "Test 1", "Test 3"]

+        "limit":
+          topic: (scope)->
+            scope = connect().find("posts")
+            scope.limit(1).all @callback
+          "should return specified number of objects": (posts)->
+            assert.equal posts.length, 1
+          "should return first results": (posts)->
+            titles = (post.title for post in posts)
+            assert.deepEqual titles, ["Test 1"]
+
+        "skip":
+          topic: (scope)->
+            scope = connect().find("posts")
+            scope.skip(1).all @callback
+          "should return specified number of objects": (posts)->
+            assert.equal posts.length, 2
+          "should skip first result": (posts)->
+            titles = (post.title for post in posts)
+            assert.deepEqual titles, ["Test 2", "Test 3"]
+
+        "limit, skip":
+          topic: (scope)->
+            scope = connect().find("posts")
+            scope.skip(1).limit(1).all @callback
+          "should return specified number of objects": (posts)->
+            assert.equal posts.length, 1
+          "should skip first result, return only one": (posts)->
+            titles = (post.title for post in posts)
+            assert.deepEqual titles, ["Test 2"]
     ###
     "Simple queries":
       "count":
@@ -583,42 +612,6 @@
       ##

-    "find all":
-      "no query":
-        topic: (connect)->
-          connect.find("posts").all @callback
-        "should return all posts": (posts)->
-          assert.equal 3, posts.length
-        "should return all fields for each post": (posts)->
-          for post in posts
-            assert post._id
-            assert post.text
-            assert post.author_id
-      "with query":
-        topic: (connect)->
-          connect.find("posts", text: "Test 2").all @callback
-        "should return only selected posts": (posts)->
-          assert.equal 1, posts.length
-        "should return all fields for each post": (posts)->
-          assert.equal posts[0].text, "Test 2"
-      "some fields":
-        topic: (connect)->
-          connect.find("posts").fields("text").all @callback
-        "should return only these fields": (posts)->
-          for post in posts
-            assert post._id
-            assert post.text
-            assert.isUndefined post.author_id
-      "with limit":
-        topic: (connect)->
-          connect.find("posts").limit(2).all @callback
-        "should return limited set": (posts)->
-          assert.equal 2, posts.length
-      "with skip":
-        topic: (connect)->
-          connect.find("posts").skip(2).all @callback
-        "should return limited set": (posts)->
-          assert.equal 1, posts.length
     ###


fix spec for rangeFomLineNumber

fix spec for rangeFomLineNumber

--- spec/helper-spec.coffee
+++ spec/helper-spec.coffee
@@ -87,8 +87,7 @@
       waitsForPromise ->
         atom.workspace.open("#{__dirname}/fixtures/something.js").then ->
           textEditor = atom.workspace.getActiveTextEditor()
-          range = helpers.rangeFromLineNumber(textEditor, 1) # 0 indexed
-          colStart = 4
+          range = helpers.rangeFromLineNumber(textEditor, 1, 4) # 0 indexed
           expect(range instanceof Array).toBe(true)
           expect(range[0] instanceof Array).toBe(true)
           expect(range[1] instanceof Array).toBe(true)
@@ -96,6 +95,7 @@
           expect(range[0][1]).toEqual(4)
           expect(range[1][0]).toEqual(1)
           expect(range[1][1]).toEqual(41)
+
   describe '::parse', ->
     it 'cries when no argument is passed', ->
       expect ->


Remove modified check on fixture file

Remove modified check on fixture file

--- spec/spec-helper.coffee
+++ spec/spec-helper.coffee
@@ -279,11 +279,3 @@
     event.initTextEvent('textInput', true, true, window, data)
     event = $.event.fix(event)
     $(this).trigger(event)
-
-if process.platform is 'win32'
-  unmodifiedHash = '15b13ffbc8d1c1ba5af845e5902ae67c'
-else
-  unmodifiedHash = "dd38087d0d7e3e4802a6d3f9b9745f2b"
-
-unless fs.md5ForPath(require.resolve('./fixtures/sample.js')) is unmodifiedHash
-  throw new Error("Sample.js is modified")


fixed bug in example api

fixed bug in example api

--- coffee/example_api.coffee
+++ coffee/example_api.coffee
@@ -196,14 +196,14 @@
     actions:
       get:
         method: "GET"
-        response_schema: "ref"
+        response_schema: "reference"
         status: 200

   branch:
     actions:
       get:
         method: "GET"
-        response_schema: "ref"
+        response_schema: "reference"
         status: 200
       rename:
         method: "POST"
@@ -216,7 +216,7 @@
     actions:
       get:
         method: "GET"
-        response_schema: "ref"
+        response_schema: "reference"
         status: 200
       delete:
         method: "DELETE"


Properly normalized data for job:received pusher event

--- assets/scripts/app/pusher.coffee
+++ assets/scripts/app/pusher.coffee
@@ -86,7 +86,7 @@
     switch event
       when 'build:started', 'build:finished'
         data
-      when 'job:created', 'job:started', 'job:requeued', 'job:finished', 'job:log', 'job:canceled'
+      when 'job:created', 'job:started', 'job:requeued', 'job:finished', 'job:log', 'job:canceled', 'job:received'
         data.queue = data.queue.replace('builds.', '') if data.queue
         { job: data }
       when 'worker:added', 'worker:updated', 'worker:removed'


Move events to _actions hash

Move events to _actions hash

--- assets/scripts/app/routes.coffee
+++ assets/scripts/app/routes.coffee
@@ -17,7 +17,7 @@
 # TODO: don't reopen Ember.Route to add events, there should be
 #       a better way (like "parent" resource for everything inside map)
 Ember.Route.reopen
-  events:
+  _actions:
     renderDefaultTemplate: ->
       @renderDefaultTemplate() if @renderDefaultTemplate

@@ -29,7 +29,6 @@
       else
         throw(error)

-  _actions:
     renderNoOwnedRepos: ->
       @render('no_owned_repos', outlet: 'main')


move status and health checks to public api router

--- services/web/app/coffee/router.coffee
+++ services/web/app/coffee/router.coffee
@@ -271,7 +271,7 @@
 		privateApiRouter.get '/perfTest', (req,res)->
 			res.send("hello")

-		webRouter.get '/status', (req,res)->
+		publicApiRouter.get '/status', (req,res)->
 			res.send("web sharelatex is alive (web)")
 		privateApiRouter.get '/status', (req,res)->
 			res.send("web sharelatex is alive (api)")
@@ -279,10 +279,10 @@
 		webRouter.get '/dev/csrf', (req, res) ->
 			res.send res.locals.csrfToken

-		webRouter.get '/health_check', HealthCheckController.check
+		publicApiRouter.get '/health_check', HealthCheckController.check
 		privateApiRouter.get '/health_check', HealthCheckController.check

-		webRouter.get '/health_check/redis', HealthCheckController.checkRedis
+		publicApiRouter.get '/health_check/redis', HealthCheckController.checkRedis
 		privateApiRouter.get '/health_check/redis', HealthCheckController.checkRedis

 		webRouter.get "/status/compiler/:Project_id", AuthorizationMiddlewear.ensureUserCanReadProject, (req, res) ->


index folder by default

index folder by default

--- server/lib/indexer.coffee
+++ server/lib/indexer.coffee
@@ -400,6 +400,7 @@
     actions = []
     actions.push registerNote unless indexdefinitions.note
     actions.push registerFile unless indexdefinitions.file
+    actions.push registerFolder unless indexdefinitions.folder
     async.series actions, (err) -> callback err

 ###*


tasks: don't scroll around in a circle confusing

--- salvus/page/tasks.coffee
+++ salvus/page/tasks.coffee
@@ -510,7 +510,7 @@
         if i?
             i -= 1
             if i < 0
-                i = @_visible_tasks.length - 1
+                i = 0
             @set_current_task(@_visible_tasks[i])

     set_current_task_next: () =>
@@ -518,7 +518,7 @@
         if i?
             i += 1
             if i >= @_visible_tasks.length
-                i = 0
+                i = @_visible_tasks.length - 1
             @set_current_task(@_visible_tasks[i])

     move_current_task_down: () =>


+ Factory.getCacheItem(aClass, aOptions)

--- src/cache-factory.coffee
+++ src/cache-factory.coffee
@@ -33,7 +33,13 @@
   registeredObjects = Factory._objects
   aliases = Factory._aliases

-  Factory.getCacheName = getCacheName = (aName, aOptions, cls)->
+  ###
+  # result:
+  #  * false: no cache and create a new object instance always.
+  #  * 'string': the cache name.
+  #  * undefined: no cache and using the global object instance.
+  ###
+  Factory.getCacheName = getCacheName = (aOptions, aTypeClass)->
     cached = aOptions.cached if aOptions?
     cached = cached.name if isObject(cached) and cached.name?

@@ -47,30 +53,37 @@

     if isString(cached) and cached.length
       if cached[0] isnt '/'
-        cls = Factory.registeredClass aName unless cls
-        cached = path.join(Factory.path(cls), cached)
+        #aTypeClass = Factory.registeredClass aName unless aTypeClass
+        cached = path.join(Factory.path(aTypeClass), cached)
     cached

+
+  Factory.getCacheItem = getCacheItem = (aClass, aOptions)->
+    cached = aOptions.cached if aOptions?
+    if cached?
+      popped = cached.popped
+      cachedName = getCacheName aOptions, aClass
+      if cachedName is false
+        # create a new instance always.
+        result = createObject aClass, undefined, aOptions
+      else if isString cachedName
+        result = instanceCache.get(cachedName)
+        if result is undefined
+          result = createObject aClass, undefined, aOptions
+          cached = undefined unless isObject cached
+          instanceCache.set cachedName, result, cached
+        else if popped
+          instanceCache.del(cachedName)
+    result

   getInstance = (aName, aOptions)->
     cached = aOptions.cached if aOptions?
     if cached?
       cls = Factory.registeredClass aName
       return unless cls
-      popped = cached.popped if cached.popped?
-      cachedName = getCacheName aName, aOptions, cls
-      if cachedName is false
-        # createObject(Class, arg1, arg2) = new Class(arg1, arg2)
-        result = createObject cls, undefined, aOptions
-      else if isString cachedName
-        result = instanceCache.get(cachedName)
-        if result is undefined
-          result = createObject cls, undefined, aOptions
-          cached = undefined unless isObject cached
-          instanceCache.set cachedName, result, cached
-        else if popped
-          instanceCache.del(cachedName)
-    result = Factory._get(aName, aOptions) unless result
+      result = getCacheItem cls, aOptions
+    else
+      result = Factory._get(aName, aOptions)
     return result


log problem with render/compile

log problem with render/compile

--- server/view_engine.coffee
+++ server/view_engine.coffee
@@ -23,7 +23,9 @@
     return cb(err) if (err)

     templateFn = Handlebars.compile(str)
+    start = new Date
     html = templateFn(locals)
+    console.log(">>>>>>renderWithLayout.compile", new Date - start)
     cb(null, html)


added login handler test helper functions

--- servers/testhelper.coffee
+++ servers/testhelper.coffee
@@ -410,6 +410,40 @@
     return requestParams

+class LoginHandlerHelper
+
+  @generateLoginRequestBody = (opts = {}) ->
+
+    defaultBodyObject =
+      token    : ''
+      tfcode   : ''
+      username : ''
+      password : ''
+      redirect : ''
+      groupName: 'koding'
+
+    deepObjectExtend defaultBodyObject, opts
+
+    return defaultBodyObject
+
+
+  # overwrites given options in the default params
+  @generateLoginRequestParams = (opts = {}) ->
+
+    url  = generateUrl
+      route : 'Login'
+
+    body = LoginHandlerHelper.generateLoginRequestBody()
+
+    params                = { url, body }
+    defaultRequestParams  = generateDefaultParams params
+    requestParams         = deepObjectExtend defaultRequestParams, opts
+    # after deep extending object, encodes body param to a query string
+    requestParams.body    = querystring.stringify requestParams.body
+
+    return requestParams
+
+
 class RegisterHandlerHelper

   @generateRequestBody = (opts = {}) ->
@@ -451,6 +485,7 @@
   generateRandomUsername

   TeamHandlerHelper
+  LoginHandlerHelper
   RecoverHandlerHelper
   RegisterHandlerHelper
   ValidationHandlerHelper


Switch source urls on again

Switch source urls on again

Fixed with:
https://github.com/despairblue/brunch-with-chaplin/commit/a8bf36c7ac6736324b6821351f9d1a562c0f3c33
and
https://github.com/despairblue/handlebars-brunch/commit/5220e5e6a95fe1a9c6e3bd4d6a3d7dfad9cd4c3f

--- config.coffee
+++ config.coffee
@@ -30,9 +30,8 @@
     templates:
       joinTo: 'javascripts/app.js'

-  # breaks handlebars
-  # modules:
-  #   addSourceURLs: true
+  modules:
+    addSourceURLs: true

   coffeelint:
     options:


Add keybindings to toggle PDF viewer's binary-view

References: izuzak/atom-pdf-view#208

--- keymap.cson
+++ keymap.cson
@@ -98,6 +98,9 @@
 "body:not(.platform-darwin) atom-text-editor[data-grammar='source emacs lisp']":
 	"ctrl-p":         "language-emacs-lisp:run-selection"

+"body:not(.platform-darwin) .pdf-view":
+	"ctrl-b":         "pdf-view:toggle-binary-view"
+
 "body:not(.platform-darwin) .find-and-replace,
  body:not(.platform-darwin) .project-find":
 	"ctrl-r":         "find-and-replace:toggle-regex-option"
@@ -157,6 +160,9 @@
 "body.platform-darwin atom-text-editor[data-grammar='source emacs lisp']":
 	"cmd-p":          "language-emacs-lisp:run-selection"

+"body.platform-darwin .pdf-view":
+	"cmd-b":          "pdf-view:toggle-binary-view"
+
 "body.platform-darwin .find-and-replace,
  body.platform-darwin .project-find":
 	"cmd-r":          "find-and-replace:toggle-regex-option"


Updated SVG gulp task

Updated SVG gulp task

--- gulp/images.coffee
+++ gulp/images.coffee
@@ -29,10 +29,9 @@
     .pipe g.dest "web/images"
     .pipe g.p.tap (file, t) ->
       file.contents = new Buffer(file.contents.toString()
-        .replace '<?xml version="1.0"?>\n', ''
         .replace(
-          /svg class="([^"]+)"/,
-          'svg class="$1 {%- if size is defined %} $1--{{ size }} {%- endif %}"'
+          /class="([^"]+)"/,
+          'class="$1 {%- if size is defined %} $1--{{ size }} {%- endif %}"'
         )
       )
     .pipe g.p.rename extname: ".svg.twig"


Se nos pasó un =

Se nos pasó un =

--- spec/javascripts/lib/mock-ajax.coffee
+++ spec/javascripts/lib/mock-ajax.coffee
@@ -44,7 +44,7 @@

 # Fake XHR for mocking Ajax Requests & Responses
-FakeXMLHttpRequest ->
+FakeXMLHttpRequest = ->
   xhr = {
     requestHeaders: {},


Fix bug with larger indexes

Fix bug with larger indexes

--- lib/assets/javascripts/nested_form_fields.js.coffee
+++ lib/assets/javascripts/nested_form_fields.js.coffee
@@ -37,7 +37,8 @@
     return false unless $.rails.allowAction($link)
     object_class = $link.data('object-class')
     delete_association_field_name = $link.data('delete-association-field-name')
-    removed_index = parseInt(delete_association_field_name.match('(\\d+\\]\\[_destroy])')[0][0])
+    console.log(delete_association_field_name);
+    removed_index = parseInt(delete_association_field_name.match('(\\d+\\]\\[_destroy])')[0].match('\\d+')[0])
     $.event.trigger("fields_removing.nested_form_fields",{object_class: object_class, delete_association_field_name: delete_association_field_name, removed_index: removed_index });
     $nested_fields_container = $link.parents(".nested_fields").first()
     $nested_fields_container.before "<input type='hidden' name='#{delete_association_field_name}' value='1' />"


Seems to be user friendlier to keep tab open per default.

--- app/assets/javascripts/app/controllers/ticket_zoom/attribute_bar.coffee
+++ app/assets/javascripts/app/controllers/ticket_zoom/attribute_bar.coffee
@@ -14,9 +14,7 @@
   constructor: ->
     super

-    @secondaryAction = 'closeTab'
-    if @overview_id
-      @secondaryAction = 'closeNextInOverview'
+    @secondaryAction = 'stayOnTab'

     @subscribeId = App.Macro.subscribe(@render)
     @render()


#258 - Fix moving tasks

#258 - Fix moving tasks

--- source/scripts/views/task_item.coffee
+++ source/scripts/views/task_item.coffee
@@ -19,6 +19,8 @@

   constructor: ->
     super
+
+    @el[0].task = @task

     @expanded = false


Include edit project

Include edit project

--- menus/project-manager.cson
+++ menus/project-manager.cson
@@ -13,6 +13,10 @@
           'command': 'project-manager:save-project'
         }
         {
+          'label': 'Edit Project',
+          'command': 'project-manager:edit-project'
+        }
+        {
           'label': 'Edit Projects',
           'command': 'project-manager:edit-projects'
         }


o Resolved merge conflict

o Resolved merge conflict

--- simple_tiles.coffee
+++ simple_tiles.coffee
@@ -63,7 +63,7 @@
         client.query "SELECT value FROM metadata WHERE name='format'", (error, result) ->
             if error != null
                 console.log error
-                process.exit 1
+                return

             if result.rows.length > 0
                 row = result.rows[0]
@@ -114,6 +114,7 @@
     current_name = current_layer['name']
     current_files = current_layer['files']
     if not current_files? or not current_name? or current_files.length == 0
+        console.log "Error loading project '#{current_name}', continuing..."
         continue

     # Connection to the mbtiles db
@@ -153,7 +154,11 @@

     format = project['format'][zoom]
     if req.params.format != format
-        res.send(404)
+        if project['default_tile']?
+            res.contentType("png")
+            res.send project['default_tile']
+         else
+            res.send(404)
         return

     db = project['database'][zoom]


Fixing a bug where the presenter binding / bound set was happening out of order

--- scripts/Americano.coffee
+++ scripts/Americano.coffee
@@ -40,8 +40,8 @@
     # The method to call to put the processing gears into action and
     # bind all the events
     bind: () ->
+        @onBind()
         @bound = true
-        @onBind()

     #####ensureBound#####
     # Makes sure the presenter is bound, if not it will call bind for you


Added comment

Added comment

--- js-library/app/js/jail_iframe/classes/icon_buttons.coffee
+++ js-library/app/js/jail_iframe/classes/icon_buttons.coffee
@@ -62,6 +62,8 @@
   constructor: (highlightElements, factId) ->
     @$highlightElements = $(highlightElements)

+    # Calculate a vertical percentage to position the icon relative
+    # to the textContainer
     # TODO: really do grouping, so we don't have to do hacks like this!
     textContainer = @_textContainer(@$highlightElements[0])
     textContainerBoundingRect = textContainer.getBoundingClientRect()


set comic alert

set comic alert

--- scripts/amazon-ec.coffee
+++ scripts/amazon-ec.coffee
@@ -97,6 +97,28 @@
     (() -> {})
   )

+setBrain = (robot , msg, query, isKindle) ->
+  # twitter とshell でプロパティが変わるため
+  user = if msg.envelope.user.hasOwnProperty('user') then msg.envelope.user.user else msg.envelope.user.name
+
+  stores = robot.brain.get(user) ? []
+
+  # 重複を除く
+  if !stores.filter((item) -> item.title is message).length
+    stores.push({title: query, kindle: isKindle})
+
+  # user のID は連番で登録する
+  i = 1
+  users = robot.brain.users()
+  while ( users[i] )
+    i++
+  # 保存
+  robot.brain.userForId(i, name: user) if !robot.brain.userForName(user) # user がいなければ保存
+  robot.brain.set user, stores
+  robot.brain.save()
+
+  msg.send "登録内容は" + stores.reduce((previous, current) -> {title:"#{previous.title},#{current.title}"}).title
+

 module.exports = (robot) ->

@@ -131,28 +153,10 @@
     newReleaseSearch msg, msg.match[2], false

   robot.respond /kindle登録(\S*) (\S+)$/i, (msg) ->
+    setBrain(robot,msg,msg.match[2], true)

-    message = msg.match[2]
-    # twitter とshell でプロパティが変わるため
-    user = if msg.envelope.user.hasOwnProperty('user') then msg.envelope.user.user else msg.envelope.user.name
-
-    stores = robot.brain.get(user) ? []
-
-    # 重複を除く
-    if !stores.filter((item) -> item.title is message).length
-      stores.push({title: message, kindle: true})
-
-    # user のID は連番で登録する
-    i = 1
-    users = robot.brain.users()
-    while ( users[i] )
-      i++
-    # 保存
-    robot.brain.userForId(i, name: user) if !robot.brain.userForName(user) # user がいなければ保存
-    robot.brain.set user, stores
-    robot.brain.save()
-
-    msg.send "登録内容は" + stores.reduce((previous, current) -> {title:"#{previous.title},#{current.title}"}).title
+  robot.respond /comic登録(\S*) (\S+)$/i, (msg) ->
+    setBrain(robot,msg,msg.match[2], false)

   robot.respond /登録内容(\S*)/i, (msg) ->
     # twitter とshell でプロパティが変わるため


group static page decoration

group static page decoration

--- client/app/MainApp/lazy/staticgroupcontroller.coffee
+++ client/app/MainApp/lazy/staticgroupcontroller.coffee
@@ -92,8 +92,18 @@

   decorateGuestStatus:->

-    @groupTitleView.addSubView new KDButtonView
+    @groupTitleView.addSubView button = new KDButtonView
       title    : "Request Access"
       cssClass : "editor-button"
       callback : =>
         @lazyDomController.requestAccess()
+
+    if KD.isLoggedIn()
+      KD.remote.api.JMembershipPolicy.byGroupSlug @groupEntryPoint, (err, policy)=>
+        log 'geldiik mi ilkin=e?'
+        if err then console.warn err
+        else unless policy?.approvalEnabled
+          log 'geldiik mi?'
+          button.setTitle "Join Group"
+          button.setCallback =>
+            @lazyDomController.openPath "/#{@groupEntryPoint}/Activity"


Texture: Allow different raw_pixels formats and types.

--- engine/texture.coffee
+++ engine/texture.coffee
@@ -114,12 +114,16 @@
                 return @promise
             @promised_data = raw_pixels
             @promise = new Promise (resolve, reject) =>
-                {@width=0, @height=0, pixels} = raw_pixels
+                {@width=0, @height=0, pixels, format, internal_format} = raw_pixels
                 if @width==0 or @height==0
                     reject "Texture #{name} has no width or height."
                 buffer = pixels.buffer or (new Uint8Array(pixels)).buffer
                 @texture_type = 'arrays'
                 @gl_format = @gl_internal_format = gl.RGBA
+                if format
+                    @gl_format = gl[format]
+                if internal_format
+                    @gl_internal_format = gl[internal_format]
                 @gl_type = gl.UNSIGNED_BYTE
                 if pixels.constructor == Float32Array
                     @gl_type = gl.FLOAT
@@ -378,6 +382,11 @@
                                     has no supported formats"
         return @promise

+    bind: ->
+        if @bound_unit == -1
+            @context.render_manager.bind_texture @
+        return this
+
     # @private
     # Recreates, uploads and configures texture in GPU
     upload: ->
@@ -399,11 +408,11 @@
         switch @texture_type
             when 'arrays'
                 for array, i in @arrays
-                    gl.texImage2D(gl.TEXTURE_2D, i, @gl_internal_format,
+                    gl.texImage2D(@gl_target, i, @gl_internal_format,
                         @width>>i, @height>>i, 0, @gl_format, @gl_type,
                         array)
                 if @arrays.length == 1 and @use_mipmap
-                    gl.generateMipmap gl.TEXTURE_2D
+                    gl.generateMipmap @gl_target
             when 'compressed'
                 upload_task_id = ++@upload_task_id
                 self = this
@@ -412,7 +421,7 @@
                     for array, i in self.arrays
                         return if upload_task_id != self.upload_task_id
                         render_manager.bind_texture self
-                        gl.compressedTexImage2D(gl.TEXTURE_2D, i,
+                        gl.compressedTexImage2D(self.gl_target, i,
                             self.gl_internal_format, width>>i, height>>i, 0,
                             array)
                         # console.log 'uploaded something on frame',
@@ -425,31 +434,36 @@
                     console.error "Compressed texture #{@name}
                         doesn't have requested mipmaps."
             when 'image'
-                gl.pixelStorei gl.UNPACK_FLIP_Y_WEBGL, true
-                if @use_alpha
-                    internal = gl.RGBA
-                    format = gl.RGBA
-                    type = gl.UNSIGNED_BYTE
-                else if @context.is_webgl2
-                    internal = gl.RGB565
-                    format = gl.RGB
-                    type = gl.UNSIGNED_SHORT_5_6_5
+                if @image.pixels
+                    gl.texImage2D(@gl_target, i, gl.RGBA,
+                        @width, @height, 0, gl.RGBA, gl.UNSIGNED_BYTE,
+                        @image.pixels)
                 else
-                    internal = gl.RGB
-                    format = gl.RGB
-                    type = gl.UNSIGNED_BYTE
-                gl.texImage2D gl.TEXTURE_2D, 0, internal, format, type, @image
+                    gl.pixelStorei gl.UNPACK_FLIP_Y_WEBGL, true
+                    if @use_alpha
+                        internal = gl.RGBA
+                        format = gl.RGBA
+                        type = gl.UNSIGNED_BYTE
+                    else if @context.is_webgl2
+                        internal = gl.RGB565
+                        format = gl.RGB
+                        type = gl.UNSIGNED_SHORT_5_6_5
+                    else
+                        internal = gl.RGB
+                        format = gl.RGB
+                        type = gl.UNSIGNED_BYTE
+                    gl.texImage2D @gl_target, 0, internal, format, type, @image
                 if @use_mipmap
-                    gl.generateMipmap gl.TEXTURE_2D
+                    gl.generateMipmap @gl_target
             when 'video'
                 # TODO: Is this inefficient to do every frame?
                 # Should UVs be inverted instead?
                 gl.pixelStorei gl.UNPACK_FLIP_Y_WEBGL, true
                 # TODO: Use gl.RGB?
-                gl.texImage2D gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA,
+                gl.texImage2D @gl_target, 0, gl.RGBA, gl.RGBA,
                     gl.UNSIGNED_BYTE, @video
                 if @use_mipmap
-                    gl.generateMipmap gl.TEXTURE_2D
+                    gl.generateMipmap @gl_target
         return

     # Unloads all texture data that has been generated with load()
@@ -476,15 +490,15 @@
         @loaded = true # bind_texture requires this
         @context.render_manager.bind_texture @
         gl_linear_nearest = if @filter then gl.LINEAR else gl.NEAREST
-        gl.texParameteri gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl_linear_nearest
+        gl.texParameteri @gl_target, gl.TEXTURE_MAG_FILTER, gl_linear_nearest
         # TODO: add mipmap options to the GUI
         if @use_mipmap
             gl_linear_nearest_mipmap = if @filter then gl.LINEAR_MIPMAP_LINEAR
             else gl.NEAREST_MIPMAP_NEAREST
-            gl.texParameteri gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER,
+            gl.texParameteri @gl_target, gl.TEXTURE_MIN_FILTER,
                 gl_linear_nearest_mipmap
         else
-            gl.texParameteri gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER,
+            gl.texParameteri @gl_target, gl.TEXTURE_MIN_FILTER,
                 gl_linear_nearest
         # TODO: detect which textures need this (mostly walls, floors...)
         # and add a global switch
@@ -492,14 +506,14 @@
         if @context.MYOU_PARAMS.anisotropic_filter and ext
             # TODO: detect max anisotropy, make configurable
             # ext.TEXTURE_MAX_ANISOTROPY_EXT == 0x84FE
-            gl.texParameterf gl.TEXTURE_2D, 0x84FE, 4
+            gl.texParameterf @gl_target, 0x84FE, 4
         wrap_const = switch @wrap
             when 'C' then gl.CLAMP_TO_EDGE
             when 'R' then gl.REPEAT
             when 'M' then gl.MIRRORED_REPEAT
             else gl.REPEAT
-        gl.texParameteri gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrap_const
-        gl.texParameteri gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrap_const
+        gl.texParameteri @gl_target, gl.TEXTURE_WRAP_S, wrap_const
+        gl.texParameteri @gl_target, gl.TEXTURE_WRAP_T, wrap_const
         return @

     destroy: ->


Change option key names.

Change option key names.

--- client/ide/lib/views/chat/idechatsettingspane.coffee
+++ client/ide/lib/views/chat/idechatsettingspane.coffee
@@ -66,13 +66,13 @@
     channel = @getData()

     @startSession = new ButtonViewWithProgressBar
-      button          :
+      buttonOptions   :
         title         : 'START SESSION'
         cssClass      : 'solid green start-session'
         callback      : @bound 'initiateSession'
-      progress        :
+      progressOptions :
         title         : 'STARTING SESSION'
-      loader          :
+      loaderOptions   :
         size          : width : 20
         loaderOptions :
           color       : '#FFFFFF'


nicer error message when email is in use.

--- client/landing/app/MainApp/login/registerform.coffee
+++ client/landing/app/MainApp/login/registerform.coffee
@@ -14,7 +14,6 @@
         # defaultValue  : "xx"
         name          : "firstName"
         placeholder   : "Your first name"
-        defaultValue  : "Xerces"
         validate      :
           event       : "blur"
           rules       :
@@ -26,7 +25,6 @@
       cssClass        : "half-size"
       inputOptions    :
         name          : "lastName"
-        defaultValue  : 'KK'
         # defaultValue  : "xx"
         placeholder   : "Your last name"
         validate      :
@@ -40,7 +38,6 @@
       inputOptions    :
         name          : "email"
         placeholder   : "Your email address"
-        defaultValue  : "chris"+Math.round(Math.random()*100)+"@jraphical.com"
         validate      :
           event       : "blur"
           rules       :
@@ -83,7 +80,6 @@
         name             : "username"
         forceCase        : "lowercase"
         placeholder      : "Desired username"
-        defaultValue     : "chris"+Math.round(Math.random()*100)
         validate         :
           rules          :
             required     : yes
@@ -117,7 +113,6 @@
         name          : "password"
         type          : "password"
         placeholder   : "Create a password"
-        defaultValue  : "123123123"
         validate      :
           event       : "blur"
           rules       :
@@ -133,7 +128,6 @@
     @passwordConfirm = new LoginInputView
       cssClass        : "password-confirm"
       inputOptions    :
-        defaultValue  : "123123123"
         name          : "passwordConfirm"
         type          : "password"
         placeholder   : "Confirm your password"
@@ -177,7 +171,6 @@
         forceCase     : "lowercase"
         placeholder   : "your code..."
         # defaultValue  : "111"
-        defaultValue  : "twitterfriends"
         validate      :
           event       : "blur"
           rules       :


supports interpolation at end of key

supports interpolation at end of key

--- lib/Decorator.coffee
+++ lib/Decorator.coffee
@@ -1,4 +1,4 @@
-{getType, tandoor, interpolate} = require './util'
+{base, getType, tandoor, interpolate} = require './util'
 execute = require './execute'

 # construct a data accessor for a given key and type
@@ -31,5 +31,5 @@
     # perform data operation
     execute lifecycle, origOp, op, key, args, vars, next

-  [_..., localKey] = basekey.split ':'
+  [_..., localKey] = base(basekey).split ':'
   template accessor, localKey


component-lab: remove unused story

component-lab: remove unused story

Signed-off-by: Umut Sirin <4055abbc0e40a1254e715b952ccd414607e0b365@gmail.com>

--- client/component-lab/TrialExpireWarning/TrialExpireWarning.story.coffee
+++ client/component-lab/TrialExpireWarning/TrialExpireWarning.story.coffee
@@ -1,9 +0,0 @@
-React = require 'react'
-{ storiesOf, action } = require '@kadira/storybook'
-
-TrialExpireWarning = require './TrialExpireWarning'
-
-storiesOf 'TrialExpireWarning', module
-  .add 'default', -> <TrialExpireWarning />
-
-


client: home: machine item default ip address rendering

--- client/home/lib/virtualmachines/components/machineslist/listitem.coffee
+++ client/home/lib/virtualmachines/components/machineslist/listitem.coffee
@@ -42,6 +42,14 @@
   toggle: (event) -> @setState { isDetailOpen: not @state.isDetailOpen }

+  renderIpAddress: ->
+
+    hasIp = @props.machine.get 'ipAddress'
+    ip    = hasIp or '0.0.0.0'
+    title = if hasIp then '' else 'Actual IP address will appear here when this VM is powered on.'
+
+
{ip}

+

   renderDetailToggle: ->

@@ -56,12 +64,12 @@

-
{@props.machine.get 'ipAddress'}

           {@props.machine.get 'label'}

+        {@renderIpAddress()}

           {@props.stack.get 'title'}


Clean up UI for downloading and ready to install

--- lib/about-view.coffee
+++ lib/about-view.coffee
@@ -52,14 +52,11 @@

               @div class: 'about-updates-item app-downloading-update', outlet: 'downloadingUpdate', =>
                 @span class: 'loading loading-spinner-tiny inline-block'
-                @span class: 'about-updates-label', 'Downloading'
-                @span class: 'about-updates-version', '1.5.0'
-                @a class: 'about-updates-release-notes', 'Release Notes'
+                @span class: 'about-updates-label', 'Downloading update'

               @div class: 'about-updates-item app-update-available-to-install', outlet: 'updateAvailableToInstall', =>
                 @span class: 'about-updates-label icon icon-squirrel', 'New update'
                 @span class: 'about-updates-version', outlet: 'updateAvailableVersion', '1.5.0'
-                @span class: 'about-updates-label', 'downloaded'
                 @a class: 'about-updates-release-notes', 'Release Notes'

             @button class: 'btn about-update-action-button', outlet: 'updateActionButton', 'Check for update'


Fixing the double-implicit-call-into-implicit-object problem.

--- rewriter.coffee
+++ rewriter.coffee
@@ -174,7 +174,7 @@
           return yes if not seenSingle and token.fromThen
           seenSingle = yes if token[0] in ['IF', 'ELSE', 'UNLESS', '->', '=>']
           (not token.generated and @tokens[i - 1][0] isnt ',' and include(IMPLICIT_END, token[0]) and
-            not (token[0] is 'INDENT' and (include(IMPLICIT_BLOCK, @tag(i - 1)) or @tag(i - 2) is 'CLASS'))) or
+            not (token[0] is 'INDENT' and (include(IMPLICIT_BLOCK, @tag(i - 1)) or @tag(i - 2) is 'CLASS' or @tag(i + 1) is '{'))) or
             token[0] is 'PROPERTY_ACCESS' and @tag(i - 1) is 'OUTDENT'
         action = (token, i) ->
           idx = if token[0] is 'OUTDENT' then i + 1 else i


Fix jump header specs

Fix jump header specs

--- spec/commands/jump-to-spec.coffee
+++ spec/commands/jump-to-spec.coffee
@@ -5,6 +5,7 @@

   beforeEach ->
     waitsForPromise -> atom.workspace.open("empty.markdown")
+    waitsForPromise -> atom.packages.activatePackage("language-gfm")
     runs -> editor = atom.workspace.getActiveTextEditor()

   describe ".trigger", ->
@@ -32,6 +33,10 @@

     content content

+    ```
+    ### Code title
+    ```
+
     ## Subtitle

     content content
@@ -50,10 +55,10 @@

     it "finds previous subtitle", ->
       editor.setText(text)
-      editor.setCursorBufferPosition([6, 6])
-
-      jumpTo = new JumpTo()
-      expect(jumpTo.previousHeading()).toEqual(row: 4, column: 0)
+      editor.setCursorBufferPosition([10, 6])
+
+      jumpTo = new JumpTo()
+      expect(jumpTo.previousHeading()).toEqual(row: 8, column: 0)

     it "finds previous title", ->
       editor.setText(text)
@@ -61,6 +66,14 @@

       jumpTo = new JumpTo()
       expect(jumpTo.previousHeading()).toEqual(row: 0, column: 0)
+
+    it "skip code blocks in markdown", ->
+      editor.setText(text)
+      editor.setCursorBufferPosition([8, 6])
+
+      jumpTo = new JumpTo()
+      expect(jumpTo.previousHeading()).toEqual(row: 0, column: 0)
+

   describe ".nextHeading", ->
     text = """
@@ -71,6 +84,10 @@
     ## Subtitle

     content content
+
+    ```
+    ### Code title
+    ```
     """

     it "finds nothing if no headings", ->


update app.coffee

update app.coffee

--- root/app.coffee
+++ root/app.coffee
@@ -12,17 +12,25 @@
 <% if (yaml == true) { %>yaml         = require 'roots-yaml'<%}%>

 module.exports =
-  ignores: ['readme.md', '**/layout.*', '**/_*', '.gitignore', 'ship.*conf']
+  ignores: [
+    'readme.md'
+    '**/layout.*'
+    '**/_*'
+    '.gitignore'
+    '.git'
+    '.editorconfig'
+  ]

   extensions: [
     <%= roots_extensions %>
-    js_pipeline(files: 'assets/js/*.coffee'),
-    css_pipeline(files: 'assets/css/*.styl')
+    js_pipeline(files: 'assets/js/**'),
+    css_pipeline(files: 'assets/css/**')
   ]

   stylus:
     use: [axis(), rupture(), <%= stylus_extensions %>autoprefixer()]
     sourcemap: true
+    'include css': true

   'coffee-script':
     sourcemap: true


Fix for path not always being recognized as absolute.

--- lib/atom-utils.coffee
+++ lib/atom-utils.coffee
@@ -118,13 +118,6 @@
     cursorString   = if range? then editor.getTextInBufferRange editor.bufferRangeForScreenRange range
     return cursorString or editor.getWordUnderCursor().replace(/,/g)

-  # getStringFromCursor: (cursorPosition) ->
-  #   editor = @getEditor()
-  #   range  = editor.displayBuffer.bufferRangeForScopeAtPosition('.string', cursorPosition)
-  #   return unless range
-  #   cursorString = editor.getTextInBufferRange(editor.bufferRangeForScreenRange(range))
-  #   return cursorString
-
   currentWordIsMethod: ->
     editor           = @getEditor()
     cursor           = editor.getLastCursor()
@@ -167,7 +160,6 @@

   cacheProjectDirectories: ->
     @cacheDirectory directory for directory in @getProjectDirectories()
-    # @getProjectDirectories().forEach (directory) => @cacheDirectory directory

   # cache project roots with Directory object and project name for future lookup
   cacheDirectory: (directory) ->
@@ -184,7 +176,8 @@
     if str? then str[0] is '\'' or str[0] is '"' else false

   isAbsolutePath: (filePath) ->
-    filePath.match ABSOLUTE_PATH_PATTERN
+    index = filePath.indexOf @currentProjectName, 0
+    if index is 0 then true else false

   isRelativePath: (filePath) ->
     filePath.match RELATIVE_PATH_PATTERN


define disableSaveOnClick as a function on window (global) [fixes #1956]

--- core/app/assets/javascripts/store/checkout.js.coffee
+++ core/app/assets/javascripts/store/checkout.js.coffee
@@ -1,4 +1,4 @@
-disableSaveOnClick = ->
+@disableSaveOnClick = ->
   ($ 'form.edit_order').submit ->
     ($ this).find(':submit, :image').attr('disabled', true).removeClass('primary').addClass 'disabled'


the jview mixin was already applied to the super class

--- client/Main/avatararea/avatarareapopupnotificationslistitem.coffee
+++ client/Main/avatararea/avatarareapopupnotificationslistitem.coffee
@@ -1,6 +1,4 @@
 class PopupNotificationListItem extends NotificationListItem
-
-  JView.mixin @prototype

   constructor:(options = {}, data)->
     options.tagName        or= "li"


chat: use ParticipantHeads and ChannelParticipantModel

--- client/activity/lib/views/privatemessage/privatemessagepane.coffee
+++ client/activity/lib/views/privatemessage/privatemessagepane.coffee
@@ -17,6 +17,8 @@
 isMyPost                         = require 'app/util/isMyPost'
 fetchAccounts                    = require 'app/util/fetchAccounts'

+ParticipantHeads = require './participantheads'
+ChannelParticipantsModel = require 'activity/models/channelparticipants'

 module.exports = class PrivateMessagePane extends MessagePane
@@ -391,15 +393,12 @@

     @forwardEvent @actionsMenu, 'LeftChannel'

-    @participantsView.addSubView @heads = new KDCustomHTMLView
-      cssClass    : 'heads'
-
-    @addParticipant participant for participant in participantsPreview
-
-    @participantsView.addSubView @newParticipantButton = new KDButtonView
-      cssClass    : 'new-participant'
-      iconOnly    : yes
-      callback    : @bound 'toggleAutoCompleteInput'
+    @participantsModel = new ChannelParticipantsModel { channel }
+    @participantsView.addSubView @participantHeads = new ParticipantHeads
+
+    @participantHeads.on 'NewParticipantButtonClicked', @bound 'toggleAutoCompleteInput'
+
+    @participantsModel.addChangeListener @participantHeads.bound 'updateParticipants'

   toggleAutoCompleteInput: ->
@@ -407,7 +406,6 @@
     @emit 'NewParticipantButtonClicked'

     @autoCompleteForm.toggleClass 'active'
-    @newParticipantButton.toggleClass 'active'
     @autoComplete.getView().setFocus()  if @autoCompleteForm.hasClass 'active'

@@ -434,7 +432,9 @@

     @autoCompleteForm.inputs.recipient.addSubView @autoComplete.getView()

-    @autoComplete.on 'ItemSelected', @bound 'toggleAutoCompleteInput'
+    @autoComplete.on 'ItemSelected', =>
+      @participantHeads.resetNewButtonState()
+      @toggleAutoCompleteInput()

     @autoComplete.on 'ItemListChanged', (count) =>
       participant  = @autoComplete.getSelectedItemData()[count - 1]


'neft-bundle-builder' changes support

'neft-bundle-builder' changes support

--- cli/tasks/build/bundle.coffee
+++ cli/tasks/build/bundle.coffee
@@ -20,6 +20,13 @@
 	neftFileName = "neft-#{platform}-#{mode}.js"
 	neftFilePath = "../../bundle/neft-#{platform}-#{mode}.js"

+	testFunc = do ->
+		switch platform
+			when 'node'
+				(req) -> /^(?:neft\-|\.)/.test(req)
+			else
+				-> true
+
 	bundleBuilder
 		path: 'index.js'
 		verbose: true
@@ -29,6 +36,7 @@
 		minify: options.release
 		removeLogs: options.release
 		neftFilePath: pathUtils.resolve(__dirname, neftFilePath)
+		test: testFunc
 		, (err, file) ->
 			if err
 				return callback err


Terminal: tab opening logic moved to AppView

--- client/Terminal/vmitem.coffee
+++ client/Terminal/vmitem.coffee
@@ -21,7 +21,8 @@
         color       : '#ffffff'

     @callToAction = new KDCustomHTMLView
-      tagName : 'span'
+      tagName  : 'span'
+      cssClass : 'call-to-action'

     @notice = new KDCustomHTMLView
       tagName : 'i'
@@ -42,8 +43,6 @@
       return

     @unsetClass 'off ready'
-    # niceMessage = MESSAGE_MAP[message.toLowerCase()]
-    # @notice.updatePartial niceMessage or message

     if message is 'STARTED'
       # @loader.canvas.setColor "#1aaf5d"
@@ -101,19 +100,10 @@
         @setClass 'off'
         @notice.updatePartial 'OFF'

-    @loader.hide()

-  click : ->
+  click : -> @emit 'VMItemClicked', @getData()

-    osKite = KD.singletons.vmController.kites[@getData().hostnameAlias]
-
-    if @hasClass 'ready'
-      @emit 'VMItemClicked', @getData()
-    else if @hasClass 'off'
-      osKite?.vmOn()
-    else
-      osKite?.vmOff()

   viewAppended:JView::viewAppended


add get and put to request lib

add get and put to request lib

--- client/app/lib/request.coffee
+++ client/app/lib/request.coffee
@@ -1,4 +1,6 @@

+# Make ajax request more easy to do.
+# Expected callbacks: success and error
 exports.request = (type, url, data, callbacks) ->
     $.ajax
         type: type
@@ -7,8 +9,24 @@
         success: callbacks.success
         error: callbacks.error

+# Sends a get request with data as body
+# Expected callbacks: success and error
+exports.get = (url, callbacks) ->
+    exports.request "GET", url, null, callbacks
+
+# Sends a post request with data as body
+# Expected callbacks: success and error
 exports.post = (url, data, callbacks) ->
     exports.request "POST", url, data, callbacks

+# Sends a put request with data as body
+# Expected callbacks: success and error
+exports.put = (url, data, callbacks) ->
+    exports.request "PUT", url, data, callbacks
+
+# Sends a delete request with data as body
+# Expected callbacks: success and error
 exports.del = (url, callbacks) ->
     exports.request "DELETE", url, null, callbacks
+
+


fixed bug when checking for tag redundancy.

--- server/Translator.coffee
+++ server/Translator.coffee
@@ -43,7 +43,7 @@

     for post in Posts.find( 'parent_id': server.parent_id ).fetch()
       for key of post.tags
-        @suggested_tags.push( key ) unless key in @my_tags
+        @suggested_tags.push( key ) unless key of @my_tags

     author = Meteor.users.findOne( '_id': server.author_id )
     if author?


Show failure count at end

Show failure count at end

--- spec/atom-reporter.coffee
+++ spec/atom-reporter.coffee
@@ -69,9 +69,12 @@
   reportRunnerResults: (runner) ->
     @updateSpecCounts()
     if @failedCount == 0
-      @message.text "Success!"
-    else
-      @message.text "Game Over"
+      @message.text "Specs Passed"
+    else
+      if @failedCount is 1
+        @message.text "#{@failedCount} Failure"
+      else
+        @message.text "#{@failedCount} Failures"

   reportSuiteResults: (suite) ->


elektron: open external links in default browser

--- elektron/src/start.coffee
+++ elektron/src/start.coffee
@@ -1,4 +1,5 @@
 electron        = require 'electron'
+shell           = require 'shell'
 BrowserWindow   = electron.BrowserWindow
 ApplicationMenu = require './applicationmenu'
 IPCReporter     = require './ipcreporter'
@@ -41,3 +42,7 @@
     # in an array if your app supports multi windows, this is the time
     # when you should delete the corresponding element.
     mainWindow = null
+
+  mainWindow.webContents.on 'new-window', (e, url) ->
+    e.preventDefault()
+    shell.openExternal url


rm js init event

rm js init event

--- lib/analytics.coffee
+++ lib/analytics.coffee
@@ -30,8 +30,6 @@
   return if sd.CURRENT_USER?.type is 'Admin'

   @ga? 'send', 'pageview'
-
-  trackTimeTo 'Javascript initialized'

   # Track 15 second bounce rate
   setTimeout =>


WebVR: Fix many issues and add room scale option (defaults to true).

--- engine/webvr.coffee
+++ engine/webvr.coffee
@@ -1,29 +1,53 @@

-{vec3, quat, mat4} = require 'vmath'
+{vec3, quat, mat4, mat3} = require 'vmath'
 {CanvasScreen} = require './screen'

 class VRScreen extends CanvasScreen
-    init: (@context, @HMD, @scene) ->
+    init: (@context, @HMD, @scene, options={}) ->
         if @context.vr_screen?
             throw Error "There's a VR screen already"
+        {
+            use_room_scale_parent=true
+            use_unpredict=true
+        } = options
+        if use_room_scale_parent and not @scene.active_camera.parent?
+            throw Error "use_room_scale_parent requires the camera to have a
+                parent. Add a parent or set use_room_scale_parent to false
+                for a seated experience controller by the original camera."
+        if use_room_scale_parent \
+            and not @HMD.stageParameters?.sittingToStandingTransform?
+                use_room_scale_parent = false
         @context.vr_screen = this
         @canvas = @context.canvas
         {@framebuffer} = @context.canvas_screen
         @frame_data = new VRFrameData
-        old_pm0 = mat4.create()
+        @old_pm0 = mat4.create()
+        @left_orientation = quat.create()
+        @right_orientation = quat.create()
+        @last_time = performance.now()
+        @use_unpredict = use_unpredict and @HMD.displayName == 'OpenVR HMD'
+        @sst = mat4.create()
+        if use_room_scale_parent
+            mat4.copyArray @sst, @HMD.stageParameters.sittingToStandingTransform
+            mat4.invert @sst, @sst
+        # @to_Z_up = mat4.new 1,0,0,0, 0,0,1,0, 0,-1,0,0, 0,0,0,1
         # left eye viewport
         camera = @scene.active_camera.clone()
         camera.rotation_order = 'Q'
+        mat4.identity camera.matrix_parent_inverse
         quat.identity camera.rotation
-        @scene.make_parent @scene.active_camera, camera
+        if not use_room_scale_parent
+            @scene.make_parent @scene.active_camera, camera
         v = @add_viewport camera
         # v.set_clear false, false
         v.rect = [0, 0, 0.5, 1]
         # right eye viewport
         camera = @scene.active_camera.clone()
         camera.rotation_order = 'Q'
+        mat4.identity camera.matrix_parent_inverse
         quat.identity camera.rotation
-        @scene.make_parent @scene.active_camera, camera
+        if not use_room_scale_parent
+            @scene.make_parent @scene.active_camera, camera
         v = @add_viewport camera
         v.set_clear false, false
         v.rect = [0.5, 0, 0.5, 1]
@@ -53,34 +77,71 @@
             pose: {position, orientation},
             leftProjectionMatrix,
             rightProjectionMatrix,
+            leftViewMatrix,
+            rightViewMatrix,
         } = @frame_data
-        {position: p0, rotation: r0, projection_matrix: pm0} =
+        window.pose = @frame_data.pose
+        {position: p0, rotation: r0, world_matrix: m0, projection_matrix: pm0} =
             @viewports[0].camera
-        {position: p1, rotation: r1, projection_matrix: pm1} =
+        {position: p1, rotation: r1, world_matrix: m1, projection_matrix: pm1} =
             @viewports[1].camera
-        if position?
-            vec3.copyArray p0, position
-            vec3.copyArray p1, position
-        if orientation?
-            vec3.copyArray r0, orientation
-            vec3.copyArray r1, orientation
+        # if position?
+        #     vec3.copyArray p0, position
+        #     vec3.copyArray p1, position
+        # if orientation?
+        #     vec3.copyArray r0, orientation
+        #     vec3.copyArray r1, orientation
+
+        if leftViewMatrix?
+            m4 = mat4.create()
+            m3 = mat3.create()
+            {sst} = this
+            time = performance.now()
+            delta_frames = Math.min 10, (time - @last_time)/11.11111111
+            # it tries to predict one frame ahead using the difference between
+            # the last two frames, so we substract the prediction
+            unpredict = 1-(1/(delta_frames+1))
+
+            mat4.copyArray m4, leftViewMatrix
+            mat4.mul m4, m4, sst
+            mat4.invert m4, m4
+            mat3.fromMat4 m3, m4
+            quat.fromMat3 r0, m3
+            if @use_unpredict
+                quat.slerp r0, @left_orientation, r0, unpredict
+            quat.copy @left_orientation, r0
+            vec3.set p0, m4.m12, m4.m13, m4.m14
+
+            mat4.copyArray m4, rightViewMatrix
+            mat4.mul m4, m4, sst
+            mat4.invert m4, m4
+            mat3.fromMat4 m3, m4
+            quat.fromMat3 r1, m3
+            if @use_unpredict
+                quat.slerp r1, @right_orientation, r1, unpredict
+            quat.copy @right_orientation, r1
+            vec3.set p1, m4.m12, m4.m13, m4.m14
+
+            @last_time = time
+
+        @viewports[0].camera._update_matrices()
+        @viewports[1].camera._update_matrices()
         mat4.copyArray pm0, leftProjectionMatrix
         mat4.copyArray pm1, rightProjectionMatrix
-        # TODO: untested
-        if not mat4.equals pm0, old_pm0
+        if not mat4.equals pm0, @old_pm0
             # culling planes need to be updated
             mat4.invert @viewports[0].camera.projection_matrix_inv, pm0
             @viewports[0].camera._calculate_culling_planes()
             mat4.invert @viewports[1].camera.projection_matrix_inv, pm1
             @viewports[1].camera._calculate_culling_planes()
-            mat4.copy old_pm0, pm0
+            mat4.copy @old_pm0, pm0
         return

     post_draw: ->
         @HMD.submitFrame()

-displays = []
+displays = null
 vrdisplaypresentchange = null

 exports.has_HMD = ->
@@ -127,6 +188,8 @@
     if ctx.vr_screen?.HMD.isPresenting
         return Promise.resolve()
     HMD = null
+    if not displays?
+        return Promise.reject "Call hasVR first."
     for display in displays
         if display instanceof VRDisplay and display.capabilities.canPresent
             HMD = display
@@ -159,7 +222,7 @@
                         if options.neck_model?
                             set_neck_model ctx, options.neck_model
                         if not ctx.vr_screen?
-                            new VRScreen ctx, HMD, scene
+                            new VRScreen ctx, HMD, scene, options
                         resolve()
                     catch e
                         reject(e)


Update (and expand) text in translation. #405

--- coffeescripts/cards-en.coffee
+++ coffeescripts/cards-en.coffee
@@ -130,7 +130,7 @@
         '#aboutTab' : 'About'
         # Obstacles
         '.choose-obstacles' : 'Choose Obstacles'
-        '.choose-obstacles-description' : 'Choose up to three obstacles'
+        '.choose-obstacles-description' : 'Choose up to three obstacles to include in the permalink for use in external programs. (This feature is in BETA; support for displaying which obstacles were selected in the printout is not yet supported.)'
         '.coreasteroid0-select' : 'Core Asteroid 0'
         '.coreasteroid1-select' : 'Core Asteroid 1'
         '.coreasteroid2-select' : 'Core Asteroid 2'


K-wing translation

K-wing translation

-Translated everything form the K-Wing expansion
-Added Advanced Slam as it was missing on the list (appeared blank)
-Fixed a typo on Palpatine card, and an error from the official translation which will be surelly regarded in our regional FAQ. As it was, Palpatine couldn't be used on any dice roll made by his ship.

I still haven't had the chance to get my hands on a Kihraxz to translate its part. Soon.

--- coffeescripts/cards-es.coffee
+++ coffeescripts/cards-es.coffee
@@ -577,13 +577,15 @@
             text: """Cuando una nave aliada declare un ataque, puedes gastar una ficha de Blanco Fijado que hayas asignado al defensor para reducir su Agilidad en 1 contra el ataque declarado."""
         "Miranda Doni":
             ship: 'Ala-K'
-            text: """Once per round when attacking, you may either spend 1 shield to roll 1 additional attack die or roll 1 fewer attack die to recover 1 shield."""
+            text: """Una vez por ronda, cuando ataques, puedes elegir entre gastar 1 de Escudos para tirar 1 dado de ataque adicional o bien tirar 1 dado de ataque menos para recuperar 1 de Escudos."""
         "Esege Tuketu":
             ship: 'Ala-K'
-            text: """When another friendly ship at Range 1-2 is attacking, it may treat your focus tokens as its own."""
+            text: """Cuando otra nave aliada que tengas a alcance 1-2 esté atacando, puede usar tus fichas de Concentración."""
         "Guardian Squadron Pilot":
+            name: "Piloto del Escuadrón Guardián"
             ship: 'Ala-K'
         "Warden Squadron Pilot":
+            name: "Piloto del Escuadrón Custodio"
             ship: 'Ala-K'
         '"Redline"':
             name: '"Velocidad Terminal"'
@@ -1006,13 +1008,14 @@
             text: """Ataque (Energía): Gasta 2 de Energía de esta carta para efectuar este ataque. Si este ataque impacta, el defensor sufre 1 de daño crítico y recibe 1 ficha de Iones. Después se anulan todos los resultados de los dados."""
         "Emperor Palpatine":
             name: "Emperador Palpatine"
-            text: """%IMPERIALONLY%%LINEBREAK%Una vez por ornda, puedes cambiar el resultado de una tirada de dado efectuada por otra nave aliada por el de cualquier otro resultado posible para ese dado. El resultado de ese dado no podrá volver a ser modificado."""
+            text: """%IMPERIALONLY%%LINEBREAK%Una vez por ronda, puedes cambiar el resultado de una tirada de dado efectuada por cualquier nave aliada por el de cualquier otro resultado posible para ese dado. El resultado de ese dado no podrá volver a ser modificado."""
         "Bossk":
             text: """%SCUMONLY%%LINEBREAK%Después de que realices un ataque y falles, si no tienes fichas de Tensión debes recibir 1 ficha de Tensión. Después asigna 1 ficha de Concentración a tu nave y fija al defensor como blanco."""
         "Lightning Reflexes":
             text: """%SMALLSHIPONLY%%LINEBREAK%After you execute a white or green maneuver on your dial, you may discard this card to rotate your ship 180°.  Then receive 1 stress token after the "Check Pilot Stress" step."""
         "Twin Laser Turret":
-            text: """Attack: Perform this attack twice (even against a ship outside your firing arc).

Each time this attack hits, the defender suffers 1 damage.  Then cancel all dice results."""
+            name: "Torreta Láser Doble"
+            text: """Ataque: Efectúa este ataque dos veces (incluso contra una nave situada fuera de tu arco de fuego).

Cada vez que este ataque impacte, el defensor sufre 1 de daño. Luego se anulan todos los resultados de los dados."""
         "Plasma Torpedoes":
             name: "Torpedos de Plasma"
             text: """Ataque (Blanco fijado): Gasta tu ficha de Blanco fijado y descarta esta carta para efectuar este ataque.

Si el ataque impacta, después de inflingir daños quita 1 ficha de Escudos del defensor."""
@@ -1020,9 +1023,11 @@
             name: "Bombas de Iones"
             text: """Cuando reveles tu selector de maniobras, puedes descartar esta carta para soltar 1 ficha de Bomba de iones.

Esta ficha detona al final de la fase de Activación.

Ficha de Bomba de iones: Cuando se detona esta ficha de Bomba, toda nave que se encuentre a alcance 1 de ella recibe 2 fichas de Iones. Después se descarta esta ficha."""
         "Conner Net":
-            text: """Action: Discard this card to drop 1 Conner Net token.

When a ship's base or maneuver template overlaps this token, this token detonates.

Conner Net Token: When this bomb token detonates, the ship that moved through or overlapped this token suffers 1 damage, receives 2 ion tokens, and skips its "Perform Action" step.  Then discard this token."""
+            name: "Red Conner"
+            text: """Acción: Descarta esta carta para soltar 1 ficha de Red Conner.

Esta ficha se detona cuando la peana o plantilla de maniobra de una nave se solape con ella.

Ficha de Red Conner: Cuando es detona esta ficha de Bomba, la nave que la haya atravesado o solapado sufre 1 daño, recibe 2 fichas de Iones y se salta su paso de "Realizar una acción". Después se descarta esta ficha."""
         "Bombardier":
-            text: """When dropping a bomb, you may use the (%STRAIGHT% 2) template instead of the (%STRAIGHT% 1) template."""
+            name: "Bombardero"
+            text: """Cuando sueltes una bomba, puedes usar la plantilla (%STRAIGHT% 2) en lugar de la plantilla (%STRAIGHT% 1)."""
         "Cluster Mines":
             name: "Minas de Racimo"
             text: """Acción: Descarta esta carta para soltar 1 conjunto de Minas de racimo.

Cada ficha de Mina de racimo se detona cuando la peana o plantilla de maniobra de una nave se solapa con ella.

Ficha de Mina de racimo: Cuando se detona una de estas fichas de Bomba, la nave que la haya atravesado o solapado tira 2 dados de ataque y sufre todo el daño (%HIT%) obtenido en la tirada. Después se descarta esta ficha."""
@@ -1032,6 +1037,9 @@
         "Advanced Homing Missiles":
             name: "Misiles Rastreadores Avanzados"
             text: """Ataque (Blanco fijado): Descarta esta carta para efectuar este ataque.%LINEBREAK%Si el ataque impacta, inflinge 1 carta de Daño boca arriba al defensor. Luego se anulan todos los resultados de los dados."""
+        "Advanced SLAM":
+            name: "Motor Sublumínico Avanzado"
+            text: """Después de efectuar una acción de MASA, si no te has solapado con un obstáculo ni con otra nave, puedes llevar a cabo una acctión gratuita."""
         'Agent Kallus':
             text: '''%IMPERIALONLY%%LINEBREAK%At the start of the first round, choose 1 enemy small or large ship.  When attacking or defending against that ship, you may change 1 of your %FOCUS% results to a %HIT% or %EVADE% result.'''
         'XX-23 S-Thread Tracers':


Fix Lambda shuttles in PL.

Fix Lambda shuttles in PL.

--- coffeescripts/cards-pl.coffee
+++ coffeescripts/cards-pl.coffee
@@ -44,7 +44,7 @@
         "TIE Interceptor Expansion Pack": "Zestaw dodatkowy TIE Interceptor"
         "TIE Bomber Expansion Pack": "Zestaw dodatkowy Bombowiec TIE"
         "TIE Advanced Expansion Pack": "Zestaw dodatkowy TIE Advanced"
-        "Lambda-Class Shuttle Expansion Pack": "Zestaw dodatkowy Prom klasy Lambda"
+        "Lambda-Class Shuttle Expansion Pack": "Zestaw dodatkowy Prom typu Lambda"
         "Slave I Expansion Pack": "Zestaw dodatkowy Slave I"
         "Imperial Aces Expansion Pack": "Zestaw dodatkowy Asy Imperium"
         "Rebel Transport Expansion Pack": "Zestaw dodatkowy Rebeliancki transportowiec"
@@ -133,7 +133,7 @@
     # Rename ships
     exportObj.renameShip 'TIE Fighter', 'Myśliwiec TIE'
     exportObj.renameShip 'TIE Bomber', 'Bombowiec TIE'
-    exportObj.renameShip 'Lambda-Class Shuttle', 'Prom klasy Lambda'
+    exportObj.renameShip 'Lambda-Class Shuttle', 'Prom typu Lambda'
     exportObj.renameShip 'VT-49 Decimator', 'Decimator VT-49'
     exportObj.renameShip 'Z-95 Headhunter', 'Z-95 Łowca głów'
     exportObj.renameShip 'CR90 Corvette (Aft)', 'Korweta CR90 (rufa)'


Don't bail when releasing a type we haven't used.

--- coffeescripts/manifest.coffee
+++ coffeescripts/manifest.coffee
@@ -1496,7 +1496,11 @@
                     ((@shelf[card.type] ?= {})[card.name] ?= []).push expansion

     use: (type, name) ->
-        card = @shelf[type][name].pop()
+        try
+            card = @shelf[type][name].pop()
+        catch e
+            return false unless card?
+
         if card?
             ((@table[type] ?= {})[name] ?= []).push card
             true
@@ -1504,7 +1508,11 @@
             false

     release: (type, name) ->
-        card = @table[type][name].pop()
+        try
+            card = @table[type][name].pop()
+        catch e
+            return false unless card?
+
         if card?
             ((@shelf[type] ?= {})[name] ?= []).push card
             true


no message

no message

--- lib/chameleon.coffee
+++ lib/chameleon.coffee
@@ -69,9 +69,12 @@
     @configureGlobal.openView()

   openSourceFolder: ->
-    atom.project.setPaths(['D:/_Work/QDT/chameleon'])
+    path = atom.packages.getLoadedPackage('chameleon').path
+    atom.project.setPaths([path])

   # createProject: ->
   #   console.log 'create-project'
   #   unless @createProjectView.modalPanel.isVisible()
   #     @createProjectView.modalPanel.show()
+# @eventElement.dispatchEvent(new CustomEvent(name, bubbles: true, cancelable: true))
+# atom.views.getView(atom.workspace).dispatchEvent(new CustomEvent('chameleon:create-module', {bubbles: true, cancelable: true}))


Modified in browser at pixie.strd6.com

Modified in browser at pixie.strd6.com

--- src/animated.coffee
+++ src/animated.coffee
@@ -48,7 +48,7 @@
       }
       frames: [0]
     currentFrameIndex: 0
-    debugAnimation: true
+    debugAnimation: false
     lastUpdate: new Date().getTime()
     useTimer: false
     transform: Matrix.IDENTITY


Add some temporary logging to debug intermittent spec failure.

--- src/atom/app.coffee
+++ src/atom/app.coffee
@@ -25,8 +25,16 @@
   quit: ->
     $native.terminate null

+  windowIdCounter: 1
+
   windowOpened: (window) ->
+    id = @windowIdCounter++
+    console.log "window opened! #{id}"
+    window.id = id
     @windows.push window

   windowClosed: (window) ->
+    console.log "windowClosed #{window.id}"
+    console.log "windows length before #{@windows.length}"
     _.remove(@windows, window)
+    console.log "windows length after #{@windows.length}"


Start dependent servers first.

Start dependent servers first.

--- server/server.coffee
+++ server/server.coffee
@@ -4,12 +4,19 @@
 express = require 'express'
 http = require 'http'
 path = require 'path'
+net = require 'net'
+forever = require 'forever-monitor'
 authenticate = require 'authenticate'
+spawn = require './spawn'

 server = express()

 PORT = 3000
 PORT_TEST = PORT + 1
+
+MONGODB_PORT = 27017
+
+EVE_PORT = 5000

 # The grunt build tasks place all compiled and copied files within
 # the _public directory.
@@ -46,14 +53,19 @@
 # The API route.
 server.use '/api', api()

-# Serve static and partial files directly.
+# Serve the static files from root.
 server.get '/static/*', (req, res) ->
   path = root + req.path.replace('/static', '')
   res.sendfile path
+
+  # Serve the partial HTML files.
 server.get '/partials/*', (req, res) ->
   res.sendfile "#{root}/#{req.path}.html"

-# Serve index for all qiprofile routes.
+# Since qiprofile is an Angular Single-Page Application,
+# serve index for all quip routes. The qiprofile
+# application then resolves the URL on the client
+# and requests the partial.
 server.get '/quip*', (req, res) ->
   res.sendfile "#{root}/index.html"

@@ -66,10 +78,14 @@
 if server.get('env') is 'test'
   server.set 'port', PORT_TEST

-# Start the server.
-port = server.get 'port'
-http.createServer(server).listen port, ->
-  env = server.settings.env
-  console.log "The qiprofile server is listening on port #{port} in #{env} mode."
+# Start MongoDB, if necessary...
+spawn 'mongod', MONGODB_PORT, ->
+  # ...then the REST app...
+  spawn 'qirest', EVE_PORT, ->
+    #...then the Express server.
+    port = server.get 'port'
+    http.createServer(server).listen port, ->
+      env = server.settings.env
+      console.log "The qiprofile server is listening on port #{port} in #{env} mode."

 module.exports = server


Fix includeAssets function.

Fix includeAssets function.

--- compiler/compileAssets.coffee
+++ compiler/compileAssets.coffee
@@ -180,7 +180,7 @@
 # Export processAsset so it can be used directly, bypassing path definitions.
 compileAssets.processAsset = processAsset

-compileAssets.emitAssets = (opts) ->
+compileAssets.includeAssets = (opts) ->
     {
         project_directory
         build_directory


Verify setup health before doing git operations

--- lib/git-bridge.coffee
+++ lib/git-bridge.coffee
@@ -93,7 +93,21 @@
         [__, indexCode, workCode, p] = m
         handler(indexCode, workCode, p)

+  @_checkHealth: (callback, filepath) ->
+    unless GitCmd
+      console.trace("GitBridge method called before locateGitAnd")
+      callback(new Error("GitBridge.locateGitAnd has not been called yet"))
+      return false
+
+    unless @getActiveRepo(filepath)
+      callback(new Error("No git repository detected"))
+      return false
+
+    return true
+
   @withConflicts: (handler) ->
+    return unless @_checkHealth(handler)
+
     conflicts = []
     errMessage = []

@@ -127,6 +141,8 @@
       handler(new GitNotFoundError(errMessage.join("\n")), null)

   @isStaged: (filepath, handler) ->
+    return unless @_checkHealth(handler, filepath)
+
     staged = true

     stdoutHandler = (chunk) =>
@@ -155,6 +171,8 @@
       handler(new GitNotFoundError, null)

   @checkoutSide: (sideName, filepath, callback) ->
+    return unless @_checkHealth(callback, filepath)
+
     proc = @process({
       command: GitCmd,
       args: ['checkout', "--#{sideName}", filepath],
@@ -172,6 +190,8 @@
       callback(new GitNotFoundError)

   @add: (filepath, callback) ->
+    return unless @_checkHealth(callback, filepath)
+
     @process({
       command: GitCmd,
       args: ['add', filepath],
@@ -186,6 +206,9 @@
     })

   @isRebasing: ->
+    return unless @_checkHealth (e) ->
+      atom.notifications.addError e.message
+
     root = @_repoGitDir()
     return false unless root?


Minor typo fix in git-commit

Minor typo fix in git-commit

--- lib/git-commit.coffee
+++ lib/git-commit.coffee
@@ -64,7 +64,7 @@
     stdout: (data) ->
       # Destroy item if there are other items in this pane
       # Otherwise, destroy the pane
-      if atom.workspace.getActivePane.getItems().length > 1
+      if atom.workspace.getActivePane().getItems().length > 1
         atom.workspace.destroyActivePaneItem()
       else
         atom.workspace.destroyActivePane()


Abort before saving

Abort before saving

--- resources/assets/coffee/_classes/forum-topic-title.coffee
+++ resources/assets/coffee/_classes/forum-topic-title.coffee
@@ -80,6 +80,7 @@
     input.disabled = true
     @saveButton[0].disabled = true

+    @abort()
     @xhr = $.ajax input.dataset.url,
       method: 'PUT'
       data:


koding.coffee: revert

koding.coffee: revert

--- workers/social/lib/social/models/computeproviders/koding.coffee
+++ workers/social/lib/social/models/computeproviders/koding.coffee
@@ -9,7 +9,7 @@

 module.exports = class Koding extends ProviderInterface

-  SUPPORTED_REGIONS    = ['us-west-2', 'us-east-1', 'eu-west-1', 'ap-southeast-1']
+  SUPPORTED_REGIONS    = ['us-east-1', 'eu-west-1', 'ap-southeast-1', 'us-west-2']

   PLANS                =
     free               :


Correct memoization

Correct memoization

--- resources/assets/coffee/react/beatmap-discussions/new-discussion.coffee
+++ resources/assets/coffee/react/beatmap-discussions/new-discussion.coffee
@@ -346,7 +346,6 @@

   setMessage: (e) =>
-    @timestampCache = null
     @setState message: e.currentTarget.value

@@ -387,7 +386,15 @@
   timestamp: =>
     return unless @props.mode == 'timeline'

-    @timestampCache ?= @parseTimestamp(@state.message)
+    if @timestampCache?.message != @state.message
+      @timestampCache = null
+
+    if !@timestampCache?
+      @timestampCache =
+        message: @state.message
+        timestamp: @parseTimestamp(@state.message)
+
+    @timestampCache.timestamp

   toggleSticky: =>


Make some minor changes/tweaks to rect handling in dom_utils

--- lib/dom_utils.coffee
+++ lib/dom_utils.coffee
@@ -68,8 +68,7 @@
       else
         clientRect = @cropRectToVisible clientRect

-        if (!clientRect || clientRect.width < 3 || clientRect.height < 3)
-          continue
+        continue unless clientRect

         # eliminate invisible elements (see test_harnesses/visibility_test.html)
         computedStyle = window.getComputedStyle(element, null)
@@ -81,33 +80,42 @@

     null

+  #
+  # Bounds the rect by the current viewport dimensions. If the rect is offscreen or has a height or width < 3
+  # then null is returned instead of a rect.
+  #
   cropRectToVisible: (rect) ->
-    if (rect.top < 0)
-      rect.height += rect.top
-      rect.top = 0
-
-    if (rect.left < 0)
-      rect.width += rect.left
-      rect.left = 0
-
-    if (rect.top >= window.innerHeight - 4 || rect.left  >= window.innerWidth - 4)
+    boundedRect = Rect.create(
+      Math.max(rect.left, 0),
+      Math.max(rect.top, 0),
+      Math.min(rect.right, window.innerWidth),
+      Math.min(rect.bottom, window.innerHeight)
+    )
+    if boundedRect.width < 3 or boundedRect.height < 3
       null
     else
-      rect
+      boundedRect

+  #
+  # Get the client rects for the <area> elements in a <map> based on the position of the <img> element using
+  # the map. Returns an array of rects.
+  #
   getClientRectsForAreas: (imgClientRect, areas) ->
     rects = []
     for area in areas
       coords = area.coords.split(",").map((coord) -> parseInt(coord, 10))
       shape = area.shape.toLowerCase()
-      if shape == "rect" or coords.length == 4
+      if shape in ["rect", "rectangle"] # "rectangle" is an IE non-standard.
         [x1, y1, x2, y2] = coords
-      else if shape == "circle" or coords.length == 3
+      else if shape in ["circle", "circ"] # "circ" is an IE non-standard.
         [x, y, r] = coords
-        x1 = x - r
-        x2 = x + r
-        y1 = y - r
-        y2 = y + r
+        diff = r / Math.sqrt 2 # Gives us an inner square
+        x1 = x - diff
+        x2 = x + diff
+        y1 = y - diff
+        y2 = y + diff
+      else if shape == "default"
+        [x1, y1, x2, y2] = [0, 0, imgClientRect.width, imgClientRect.height]
       else
         # Just consider the rectangle surrounding the first two points in a polygon. It's possible to do
         # something more sophisticated, but likely not worth the effort.
@@ -116,7 +124,7 @@
       rect = Rect.translate (Rect.create x1, y1, x2, y2), imgClientRect.left, imgClientRect.top
       rect = @cropRectToVisible rect

-      rects.push {element: area, rect: rect} unless not rect or isNaN rect.top
+      rects.push {element: area, rect: rect} if rect and not isNaN rect.top
     rects

   #


Remove ref to find-mode and replace-mode

--- lib/find-view.coffee
+++ lib/find-view.coffee
@@ -65,8 +65,6 @@
   serialize: ->
     findHistory: @findHistory.serialize()
     replaceHistory: @replaceHistory.serialize()
-    showFind: @hasParent() and @hasClass('find-mode')
-    showReplace: @hasParent() and @hasClass('replace-mode')
     modelState: @findModel.serialize()

   handleEvents: ->
@@ -105,15 +103,12 @@
     atom.rootView.command 'find-and-replace:replace-all', @replaceAll

   showFind: =>
-    if not @hasParent()
-      @attach()
-      @addClass('find-mode').removeClass('replace-mode')
+    @attach() if not @hasParent()
     @findEditor.focus()
     @findEditor.selectAll()

   showReplace: =>
     @attach()
-    @addClass('replace-mode').removeClass('find-mode')
     @replaceEditor.redraw()
     @replaceEditor.focus()
     @replaceEditor.selectAll()
@@ -128,7 +123,7 @@
     super()

   toggleFocus: =>
-    if @hasClass('replace-mode') and @findEditor.find(':focus').length > 0
+    if @findEditor.find(':focus').length > 0
       @replaceEditor.focus()
     else
       @findEditor.focus()


Revert "Multiple key presses, diagonal movement. More succinct code"

This reverts commit 9979b096d3bdde7595ac0ab466c86d7750c7d667.

--- game.coffee
+++ game.coffee
@@ -9,15 +9,17 @@
 hero = null
 #- constants
 for direction in ['NORTH','SOUTH','EAST','WEST']
-  @["#{direction}"] = {NORTH: 1, SOUTH: 2, EAST: 4, WEST: 8}[direction]
-  @["DIR_#{direction}"] = new ->
-    @[direction] = true
-    @.dirname = window[direction]
-    @.movement = false
-    @.marked_for_deletion = false
-    @.delete = ->
-      @marked_for_deletion = true
-    @
+  @["#{direction}"] = direction
+  dir = @["DIR_#{direction}"] = do ->
+    {}
+  dir[direction] = true
+  dir.dirname = direction
+  dir.movement = false
+  dir.marked_for_deletion = false
+
+  dir.delete = ->
+    @marked_for_deletion = true
+

 #- helper methods
 rgba = (r,g,b,a) ->
@@ -113,7 +115,7 @@
       width: 32
       height: 32
       image_src: 'images/link.png'
-      dir: 0
+      dir: {}
       moving: false
     options.reverse_merge(option_defaults)
     @.reverse_merge(options)
@@ -138,39 +140,45 @@
     options.reverse_merge(option_defaults)

   check_moving: ->
-    if @moving
-      console.log 'moving'
-      moving_states = {}
-      moving_states[NORTH] = {x: 0, y: -1}
-      moving_states[SOUTH] = {x: 0, y: 1}
-      moving_states[EAST] = {x: 1, y: 0}
-      moving_states[WEST] = {x: -1, y: 0}
-      xo = yo = 0
-      for dir in Object.keys(moving_states)
-        if @dir & dir
-          xo += moving_states[dir].x
-          yo += moving_states[dir].y
-      @move_to @x + xo, @y + yo
+    still_moving = false
+    for dir in Object.keys(@dir)
+      if @dir[dir].marked_for_deletion
+        delete @dir[dir]
+      else
+        if @heading @dir[dir]
+          still_moving = true
+    @start_moving() if still_moving

   heading: (dir) ->
-    @dir & dir.dirname
+    @dir[dir]?.movement

   start_moving: (dir) ->
-    @dir = switch dir.dirname | @dir
-      when 3,12 then dir
-      else dir.dirname | @dir
-    @moving = true
+    if dir?
+      @dir[dir.dirname] = dir
+      @dir[dir.dirname].movement = true
+    else
+      for stale_dir in Object.keys(@dir)
+        if !@dir[stale_dir].movement
+          @dir[stale_dir].delete()
+
+    movement_values =
+      NORTH: {x: 0, y: -2}
+      SOUTH: {x: 0, y: 2}
+      EAST: {x: 2, y: 0}
+      WEST: {x: -2, y: 0}
+
+    x_off = y_off = 0
+    for mdir of @dir
+      if @dir[mdir].movement
+        console.log mdir
+        x_off += movement_values[mdir].x
+        y_off += movement_values[mdir].y
+    @move_to @x + x_off, @y + y_off

   stop_moving: (dir)->
-    if (dir.EAST? and @heading(WEST)) or (dir.WEST? and @heading(EAST)) or (dir.NORTH? and @heading(SOUTH)) or (dir.SOUTH? and @heading(NORTH))
-      @dir = dir.dirname
-      return
-    @dir = switch pending_dir = @dir ^ dir.dirname
-      when 0
-        console.log 'stopped'
-        @moving = false
-        dir.dirname
-      else pending_dir
+    if old_dir = @dir[dir.dirname]
+      #old_dir.movement=false
+      true

 #- map
 map_data = [1, 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,1,


Use production DB

Use production DB

--- config/main.production.coffee
+++ config/main.production.coffee
@@ -6,7 +6,7 @@
 version = "0.9.9a" #fs.readFileSync nodePath.join(__dirname, '../.revision'), 'utf-8'

 # PROD
-mongo = 'PROD-koding:34W4BXx595ib3J72k5Mh@db-m0.prod.aws.koding.com:27017/beta_koding'
+mongo = 'PROD-koding:34W4BXx595ib3J72k5Mh@web0.beta.system.aws.koding.com:27017/beta_koding'

 # RabbitMQ Host
 rabbit_host = 'rabbit-a.prod.aws.koding.com'


The naive AI. Playing against the PC is now possible.

--- scripts/controllers/game.coffee
+++ scripts/controllers/game.coffee
@@ -1,4 +1,5 @@
 getRandomInt = (min, max)->Math.floor(Math.random() * (max - min + 1)) + min
+object_keys = (object)-> key for own key of object

 class Board
   empty_grid = (size)-> new Array(size) for [0...size]
@@ -91,11 +92,78 @@

     result

+class AIPlayerMinMax extends Player
+  decide_move: (board_grid)->
+    size = board_grid.length
+    players = {}
+
+    possible_moves = []
+    for row, i in board_grid
+      for item, k in row
+        if not item #an empty field
+          possible_moves.push [i,k]
+        else if item isnt @symbol
+          players[item] = true
+
+    players = object_keys(players)
+
+    players.push @symbol
+
+    analyze_vector_as = (symbol, start_x, start_y, dx, dy)->
+      distance = 0
+      result = 0
+
+      while distance < size
+        x = start_x + dx * distance
+        y = start_y + dy * distance
+
+        if not board_grid[x][y]
+          result +=1
+        else if board_grid[x][y] is symbol
+          result +=3
+        else
+          return 0
+
+        distance++
+
+      if result == ((size-1) * 3 + 1) # this vector is potential win, i.e. size-1 items were found
+        result *= 3
+
+
+      result
+
+    analyze_point_as = (symbol, x, y) ->
+      result = 0
+      result += analyze_vector_as(symbol, 0, y, 1, 0)
+      result += analyze_vector_as(symbol, x, 0, 0, 1)
+      if x == y
+        result += analyze_vector_as(symbol, 0, 0, 1, 1)
+      if (x + y) == (size - 1)
+        result += analyze_vector_as(symbol, size-1, 0, -1, 1)
+
+      result
+
+    analyze_point = (x, y) ->
+      result = 0
+      for symbol in players
+        result = Math.max result, analyze_point_as(symbol, x, y)
+
+      result
+
+    max_value = -1
+    for move in possible_moves
+      if (value = analyze_point(move[0], move[1])) > max_value
+        [@x, @y] = move
+        max_value = value
+
+    @attempt_move()
+
 class Game

   @possible_types: [
     'hotseat'
     'you vs chaos'
+    'you vs pc'
   ]

   @possible_players: "xo"
@@ -142,6 +210,11 @@
         while available_symbols.length > 0
           @players.push(new AIPlayerChaosMonkey(next_random_symbol(), move_handler))

+      when 'you vs pc'
+        @players.push(new LocalPlayer(next_random_symbol(), move_handler))
+        while available_symbols.length > 0
+          @players.push(new AIPlayerMinMax(next_random_symbol(), move_handler))
+

     @start_game()


fix chrome by specifying default int precision

--- jax-core/lib/assets/javascripts/jax/shader.js.coffee
+++ jax-core/lib/assets/javascripts/jax/shader.js.coffee
@@ -120,7 +120,7 @@
       body = definitions + body

     if body.indexOf('precision') is -1
-      "precision mediump float;\n\n" + body
+      "precision mediump float;\nprecision mediump int;\n\n" + body
     else body

   mergeVariables: (parser, map) ->


[DRY] Added config to `dynamicHelpers`  and fixed path of `config.js` in the require.cache action, also fixed the load of new themes without restart the server

--- main.coffee
+++ main.coffee
@@ -1,10 +1,10 @@
 # Requires and Variables
+fs = require 'fs'
 exp = require 'express'
+pass = require 'passport'
 app = exp.createServer()
-pass = require 'passport'
+config = require('./config').config
 Local = require('passport-local').Strategy
-fs = require 'fs'
-config = require('./config').config

 # Controllers
 posts = require('./controllers/posts').posts
@@ -25,6 +25,11 @@
 # Theme Paths
 admin = __dirname + '/views/' + config.theme.admin + '/'
 front = __dirname + '/views/' + config.theme.front + '/'
+
+# To reload assets
+load = ->
+  admin = __dirname + '/views/' + config.theme.admin + '/'
+  front = __dirname + '/views/' + config.theme.front + '/'

 # App Configuration
 app.configure () ->
@@ -40,10 +45,14 @@
   app.use pass.initialize()
   app.use pass.session()
   app.use app.router
-
+
+# Exposing config in requests
+app.dynamicHelpers
+  config : -> config
+
 # Run App
 app.listen config.site.port, () ->
-  console.log 'Server running at port ' + app.address().port
+  console.log ':: CouchPress running at port ' + app.address().port + ' ::'

 # Routing
 app.get '/', (req, res) ->
@@ -52,7 +61,6 @@
             locals:
               title: config.site.title + ' / Home'
               articles: docs
-              config: config

 app.get '/:id?', (req, res, next) ->
   posts.findById req.params.id, (err, docs) ->
@@ -63,7 +71,6 @@
         locals:
           title: config.site.title + ' / ' + docs.title
           article: docs
-          config: config
     else
       next()

@@ -72,7 +79,6 @@
     locals:
       title: config.site.title + ' / Login'
       message: req.flash('error')
-      config: config

 app.post '/login', pass.authenticate 'local',
   successRedirect: '/admin'
@@ -82,7 +88,6 @@
 app.get '/register', (req, res) ->
   res.render front + 'register',
     title: config.site.title + ' / Register'
-    config: config

 app.post '/register', (req, res) ->
   data =
@@ -98,7 +103,6 @@
         locals:
           title: config.site.title
           message: JSON.stringify err
-          config: config
     else
       res.redirect '/admin'

@@ -155,7 +159,6 @@
     layout: admin + 'layout'
     locals:
       title: 'Settings'
-      config: config
       admin: adminConfig

 app.post '/admin/settings', users.check, (req, res) ->
@@ -166,9 +169,10 @@
     if err
       res.send 'Error:' + err
     else
+      delete require.cache[require.resolve('./config.js')]
+      config = require('./config').config
+      load()
       res.send 'Success! Settings Saved.'
-      delete require.cache[require.resolve('./config')]
-      config = require('./config').config

 app.get '/admin/delete/:id?', users.check, (req, res) ->
   posts.findById req.params.id, (err, docs) ->
@@ -188,7 +192,6 @@
         locals:
           title: 'Edit'
           article: docs
-          config: config

 app.post '/admin/new', users.check, (req, res) ->
   slug = sluggify req.param 'title'
@@ -235,7 +238,6 @@
         title: '500'
         created_at: ''
         body: "Internal Server Error"
-      config: config

 app.get '*', (req, res) ->
   res.render front + 'view'
@@ -245,7 +247,6 @@
         title: '404'
         created_at: ''
         body: "Woops! Can't find what you're looking for!"
-      config: config

 # Other Functions
 sluggify = (title) ->


1.2.1 release

1.2.1 release

--- main.coffee
+++ main.coffee
@@ -4,32 +4,32 @@
   constraints: ($) ->
     if @options.constraints.verticalTop
       $ "// align #{@name} and superview to top"
-      $ "view.addConstraint(NSLayoutConstraint(item: #{@name}, attribute: .Top, relatedBy: .Equal, toItem: #{@options.superviewName}, attribute: .Top, multiplier: 1.0, constant: 0.0))"
+      $ "#{@options.superviewName}.addConstraint(NSLayoutConstraint(item: #{@name}, attribute: .Top, relatedBy: .Equal, toItem: #{@options.superviewName}, attribute: .Top, multiplier: 1.0, constant: 0.0))"
       $.newline()

     if @options.constraints.verticalCenter
       $ "// center #{@name} vertically in superview"
-      $ "view.addConstraint(NSLayoutConstraint(item: #{@name}, attribute: .CenterY, relatedBy: .Equal, toItem: #{@options.superviewName}, attribute: .CenterY, multiplier: 1.0, constant: 0.0))"
+      $ "#{@options.superviewName}.addConstraint(NSLayoutConstraint(item: #{@name}, attribute: .CenterY, relatedBy: .Equal, toItem: #{@options.superviewName}, attribute: .CenterY, multiplier: 1.0, constant: 0.0))"
       $.newline()

     if @options.constraints.verticalBottom
       $ "// align #{@name} and superview to bottom"
-      $ "view.addConstraint(NSLayoutConstraint(item: #{@name}, attribute: .Bottom, relatedBy: .Equal, toItem: #{@options.superviewName}, attribute: .Bottom, multiplier: 1.0, constant: 0.0))"
+      $ "#{@options.superviewName}.addConstraint(NSLayoutConstraint(item: #{@name}, attribute: .Bottom, relatedBy: .Equal, toItem: #{@options.superviewName}, attribute: .Bottom, multiplier: 1.0, constant: 0.0))"
       $.newline()

     if @options.constraints.horizontalLeft
       $ "// align #{@name} and superview to left"
-      $ "view.addConstraint(NSLayoutConstraint(item: #{@name}, attribute: .Left, relatedBy: .Equal, toItem: #{@options.superviewName}, attribute: .Left, multiplier: 1.0, constant: 0.0))"
+      $ "#{@options.superviewName}.addConstraint(NSLayoutConstraint(item: #{@name}, attribute: .Left, relatedBy: .Equal, toItem: #{@options.superviewName}, attribute: .Left, multiplier: 1.0, constant: 0.0))"
       $.newline()

     if @options.constraints.horizontalCenter
       $ "// center #{@name} horizontally in superview"
-      $ "view.addConstraint(NSLayoutConstraint(item: #{@name}, attribute: .CenterX, relatedBy: .Equal, toItem: #{@options.superviewName}, attribute: .CenterX, multiplier: 1.0, constant: 0.0))"
+      $ "#{@options.superviewName}.addConstraint(NSLayoutConstraint(item: #{@name}, attribute: .CenterX, relatedBy: .Equal, toItem: #{@options.superviewName}, attribute: .CenterX, multiplier: 1.0, constant: 0.0))"
       $.newline()

     if @options.constraints.horizontalRight
       $ "// align #{@name} and superview to right"
-      $ "view.addConstraint(NSLayoutConstraint(item: #{@name}, attribute: .Right, relatedBy: .Equal, toItem: #{@options.superviewName}, attribute: .Right, multiplier: 1.0, constant: 0.0))"
+      $ "#{@options.superviewName}.addConstraint(NSLayoutConstraint(item: #{@name}, attribute: .Right, relatedBy: .Equal, toItem: #{@options.superviewName}, attribute: .Right, multiplier: 1.0, constant: 0.0))"
       $.newline()

   render: ($) ->
@@ -41,7 +41,7 @@
       str += "[#{@name}]"
       str += "-#{@bounds.bottom}-|" if @options.margin.bottom
       $ "// align #{@name} from the top and bottom"
-      $ "view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat(\"#{str}\", options: .DirectionLeadingToTrailing, metrics: nil, views: [\"#{@name}\": #{@name}]))"
+      $ "#{@options.superviewName}.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat(\"#{str}\", options: .DirectionLeadingToTrailing, metrics: nil, views: [\"#{@name}\": #{@name}]))"
       $.newline()

     if @options.margin.left or @options.margin.right
@@ -50,17 +50,17 @@
       str += "[#{@name}]"
       str += "-#{@bounds.right}-|" if @options.margin.right
       $ "// align #{@name} from the left and right"
-      $ "view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat(\"#{str}\", options: .DirectionLeadingToTrailing, metrics: nil, views: [\"#{@name}\": #{@name}]))"
+      $ "#{@options.superviewName}.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat(\"#{str}\", options: .DirectionLeadingToTrailing, metrics: nil, views: [\"#{@name}\": #{@name}]))"
       $.newline()

     if @options.dimensions.width
       $ '// width constraint'
-      $ "view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat(\"H:[#{@name}(==#{@bounds.width})]\", options: .DirectionLeadingToTrailing, metrics: nil, views: [\"#{@name}\": #{@name}]))"
+      $ "#{@options.superviewName}.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat(\"H:[#{@name}(==#{@bounds.width})]\", options: .DirectionLeadingToTrailing, metrics: nil, views: [\"#{@name}\": #{@name}]))"
       $.newline()

     if @options.dimensions.height
       $ '// height constraint'
-      $ "view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat(\"V:[#{@name}(==#{@bounds.height})]\", options: .DirectionLeadingToTrailing, metrics: nil, views: [\"#{@name}\": #{@name}]))"
+      $ "#{@options.superviewName}.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat(\"V:[#{@name}(==#{@bounds.height})]\", options: .DirectionLeadingToTrailing, metrics: nil, views: [\"#{@name}\": #{@name}]))"
       $.newline()

 exports.renderClass = Swift