joosy 0.1.0.RC1 → 0.1.0.RC2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Gemfile +1 -0
- data/Gemfile.lock +8 -1
- data/MIT-LICENSE +2 -2
- data/README.md +89 -0
- data/app/assets/javascripts/joosy/core/application.js.coffee +25 -5
- data/app/assets/javascripts/joosy/core/form.js.coffee +212 -22
- data/app/assets/javascripts/joosy/core/helpers.js.coffee +11 -1
- data/app/assets/javascripts/joosy/core/joosy.js.coffee +22 -17
- data/app/assets/javascripts/joosy/core/layout.js.coffee +17 -7
- data/app/assets/javascripts/joosy/core/modules/container.js.coffee +19 -15
- data/app/assets/javascripts/joosy/core/modules/events.js.coffee +10 -9
- data/app/assets/javascripts/joosy/core/modules/filters.js.coffee +16 -12
- data/app/assets/javascripts/joosy/core/modules/log.js.coffee +8 -5
- data/app/assets/javascripts/joosy/core/modules/module.js.coffee +31 -21
- data/app/assets/javascripts/joosy/core/modules/renderer.js.coffee +114 -51
- data/app/assets/javascripts/joosy/core/modules/time_manager.js.coffee +2 -2
- data/app/assets/javascripts/joosy/core/modules/widgets_manager.js.coffee +10 -10
- data/app/assets/javascripts/joosy/core/page.js.coffee +31 -21
- data/app/assets/javascripts/joosy/core/preloader.js.coffee +3 -3
- data/app/assets/javascripts/joosy/core/resource/collection.js.coffee +137 -0
- data/app/assets/javascripts/joosy/core/resource/generic.js.coffee +178 -13
- data/app/assets/javascripts/joosy/core/resource/rest.js.coffee +167 -44
- data/app/assets/javascripts/joosy/core/resource/rest_collection.js.coffee +100 -32
- data/app/assets/javascripts/joosy/core/router.js.coffee +23 -25
- data/app/assets/javascripts/joosy/core/templaters/rails_jst.js.coffee +19 -3
- data/app/assets/javascripts/joosy/core/widget.js.coffee +7 -9
- data/app/assets/javascripts/joosy/preloaders/caching.js.coffee +117 -57
- data/app/assets/javascripts/joosy/preloaders/inline.js.coffee +23 -24
- data/app/helpers/joosy/sprockets_helper.rb +1 -1
- data/lib/joosy/forms.rb +2 -12
- data/lib/joosy/rails/version.rb +1 -1
- data/lib/rails/generators/joosy/templates/app/pages/template.js.coffee +1 -1
- data/lib/rails/generators/joosy/templates/app/resources/template.js.coffee +1 -1
- data/spec/javascripts/joosy/core/form_spec.js.coffee +55 -12
- data/spec/javascripts/joosy/core/layout_spec.js.coffee +1 -1
- data/spec/javascripts/joosy/core/modules/container_spec.js.coffee +0 -1
- data/spec/javascripts/joosy/core/modules/module_spec.js.coffee +1 -1
- data/spec/javascripts/joosy/core/modules/renderer_spec.js.coffee +39 -3
- data/spec/javascripts/joosy/core/modules/time_manager_spec.js.coffee +1 -1
- data/spec/javascripts/joosy/core/page_spec.js.coffee +4 -1
- data/spec/javascripts/joosy/core/resource/collection_spec.js.coffee +84 -0
- data/spec/javascripts/joosy/core/resource/generic_spec.js.coffee +86 -3
- data/spec/javascripts/joosy/core/resource/rest_collection_spec.js.coffee +15 -22
- data/spec/javascripts/joosy/core/resource/rest_spec.js.coffee +27 -4
- data/spec/javascripts/joosy/core/widget_spec.js.coffee +3 -14
- metadata +21 -19
- data/README.rdoc +0 -3
| @@ -4,37 +4,39 @@ Joosy.Modules.Container = | |
| 4 4 |  | 
| 5 5 | 
             
              eventSplitter: /^(\S+)\s*(.*)$/
         | 
| 6 6 |  | 
| 7 | 
            -
              $: (selector) -> | 
| 7 | 
            +
              $: (selector) ->
         | 
| 8 | 
            +
                $(selector, @container)
         | 
| 8 9 |  | 
| 9 10 | 
             
              refreshElements: ->
         | 
| 10 11 | 
             
                @__collectElements().each (key, value) =>
         | 
| 12 | 
            +
                  # TODO: Check for possible collisions?
         | 
| 11 13 | 
             
                  @[key] = @$(value)
         | 
| 12 14 |  | 
| 13 15 | 
             
              swapContainer: (container, data) ->
         | 
| 14 | 
            -
                 | 
| 15 | 
            -
                container. | 
| 16 | 
            -
                 | 
| 16 | 
            +
                container.unbind().off()
         | 
| 17 | 
            +
                container.html data
         | 
| 18 | 
            +
                container
         | 
| 17 19 |  | 
| 18 20 | 
             
              __collectElements: ->
         | 
| 19 | 
            -
                elements = Object.extended | 
| 21 | 
            +
                elements = Object.extended @elements || {}
         | 
| 20 22 |  | 
| 21 23 | 
             
                klass = this
         | 
| 22 24 | 
             
                while klass = klass.constructor.__super__
         | 
| 23 | 
            -
                   | 
| 25 | 
            +
                  Joosy.Module.merge elements, klass.elements, false
         | 
| 24 26 |  | 
| 25 27 | 
             
                elements
         | 
| 26 28 |  | 
| 27 29 | 
             
              __collectEvents: ->
         | 
| 28 | 
            -
                events = Object.extended | 
| 30 | 
            +
                events = Object.extended @events || {}
         | 
| 29 31 |  | 
| 30 32 | 
             
                klass = this
         | 
| 31 33 | 
             
                while klass = klass.constructor.__super__
         | 
| 32 | 
            -
                   | 
| 34 | 
            +
                  Joosy.Module.merge events, klass.events, false
         | 
| 33 35 |  | 
| 34 36 | 
             
                events
         | 
| 35 37 |  | 
| 36 38 | 
             
              __extractSelector: (selector) ->
         | 
| 37 | 
            -
                if r = selector.match | 
| 39 | 
            +
                if r = selector.match /\$([A-z]+)/
         | 
| 38 40 | 
             
                  selector = @__collectElements()[r[1]]
         | 
| 39 41 |  | 
| 40 42 | 
             
                selector
         | 
| @@ -44,16 +46,18 @@ Joosy.Modules.Container = | |
| 44 46 | 
             
                events = @__collectEvents()
         | 
| 45 47 |  | 
| 46 48 | 
             
                events.each (key, method) =>
         | 
| 47 | 
            -
                   | 
| 48 | 
            -
             | 
| 49 | 
            +
                  unless Object.isFunction method
         | 
| 50 | 
            +
                    method = @[method]
         | 
| 51 | 
            +
                  callback = (event) ->
         | 
| 52 | 
            +
                    method.call module, this, event
         | 
| 49 53 |  | 
| 50 | 
            -
                  match      = key.match | 
| 54 | 
            +
                  match      = key.match @eventSplitter
         | 
| 51 55 | 
             
                  eventName  = match[1]
         | 
| 52 | 
            -
                  selector   = @__extractSelector | 
| 56 | 
            +
                  selector   = @__extractSelector match[2]
         | 
| 53 57 |  | 
| 54 58 | 
             
                  if selector == ""
         | 
| 55 | 
            -
                    @container.bind | 
| 59 | 
            +
                    @container.bind eventName, callback
         | 
| 56 60 | 
             
                    Joosy.Modules.Log.debugAs @, "#{eventName} binded on container"
         | 
| 57 61 | 
             
                  else
         | 
| 58 | 
            -
                    @container.on | 
| 62 | 
            +
                    @container.on eventName, selector, callback
         | 
| 59 63 | 
             
                    Joosy.Modules.Log.debugAs @, "#{eventName} binded on #{selector}"
         | 
| @@ -1,12 +1,12 @@ | |
| 1 1 | 
             
            Joosy.Modules.Events =
         | 
| 2 2 | 
             
              wait: (events, callback) ->
         | 
| 3 | 
            -
                events = events.split | 
| 3 | 
            +
                events = events.split /\s+/
         | 
| 4 4 |  | 
| 5 5 | 
             
                @__oneShotEvents ||= []
         | 
| 6 6 | 
             
                @__oneShotEvents.push [events, callback]
         | 
| 7 7 |  | 
| 8 8 | 
             
              bind: (events, callback) ->
         | 
| 9 | 
            -
                events = events.split | 
| 9 | 
            +
                events = events.split /\s+/
         | 
| 10 10 |  | 
| 11 11 | 
             
                @__boundEvents ||= []
         | 
| 12 12 | 
             
                @__boundEvents.push [events, callback]
         | 
| @@ -14,22 +14,23 @@ Joosy.Modules.Events = | |
| 14 14 | 
             
              unbind: (target) ->
         | 
| 15 15 | 
             
                for [events, callback], index in @__boundEvents
         | 
| 16 16 | 
             
                  if callback == target
         | 
| 17 | 
            -
                    @__boundEvents.splice | 
| 17 | 
            +
                    @__boundEvents.splice index, 1
         | 
| 18 18 | 
             
                    return
         | 
| 19 19 |  | 
| 20 20 | 
             
              trigger: (event) ->
         | 
| 21 21 | 
             
                Joosy.Modules.Log.debugAs @, "Event #{event} triggered"
         | 
| 22 22 | 
             
                if @__oneShotEvents
         | 
| 23 23 | 
             
                  for [events, callback], index in @__oneShotEvents
         | 
| 24 | 
            -
                    position = events.indexOf | 
| 25 | 
            -
                     | 
| 24 | 
            +
                    position = events.indexOf event
         | 
| 25 | 
            +
                    if position >= 0
         | 
| 26 | 
            +
                      events.splice position, 1
         | 
| 26 27 |  | 
| 27 28 | 
             
                    if events.length == 0
         | 
| 28 | 
            -
                      @__oneShotEvents.splice | 
| 29 | 
            +
                      @__oneShotEvents.splice index, 1
         | 
| 29 30 |  | 
| 30 31 | 
             
                      callback()
         | 
| 31 32 |  | 
| 32 33 | 
             
                if @__boundEvents
         | 
| 33 | 
            -
                  for [ | 
| 34 | 
            -
                    if events.has | 
| 35 | 
            -
                      callback()
         | 
| 34 | 
            +
                  for [events, callback] in @__boundEvents
         | 
| 35 | 
            +
                    if events.has event
         | 
| 36 | 
            +
                      callback()
         | 
| @@ -1,39 +1,43 @@ | |
| 1 1 | 
             
            Joosy.Modules.Filters =
         | 
| 2 2 | 
             
              included: ->
         | 
| 3 3 | 
             
                @beforeLoad = (callback) ->
         | 
| 4 | 
            -
                  unless @::hasOwnProperty | 
| 4 | 
            +
                  unless @::hasOwnProperty '__beforeLoads'
         | 
| 5 5 | 
             
                    @::__beforeLoads = [].concat @.__super__.__beforeLoads || []
         | 
| 6 6 | 
             
                  @::__beforeLoads.push callback
         | 
| 7 7 |  | 
| 8 8 | 
             
                @afterLoad = (callback) ->
         | 
| 9 | 
            -
                  unless @::hasOwnProperty | 
| 9 | 
            +
                  unless @::hasOwnProperty '__afterLoads'
         | 
| 10 10 | 
             
                    @::__afterLoads = [].concat @.__super__.__afterLoads || []
         | 
| 11 11 | 
             
                  @::__afterLoads.push callback
         | 
| 12 12 |  | 
| 13 13 | 
             
                @afterUnload = (callback) ->
         | 
| 14 | 
            -
                  unless @::hasOwnProperty | 
| 14 | 
            +
                  unless @::hasOwnProperty '__afterUnloads'
         | 
| 15 15 | 
             
                    @::__afterUnloads = [].concat @.__super__.__afterUnloads || []
         | 
| 16 16 | 
             
                  @::__afterUnloads.push callback
         | 
| 17 17 |  | 
| 18 18 | 
             
              __runBeforeLoads: (opts...) ->
         | 
| 19 | 
            -
                 | 
| 19 | 
            +
                unless @__beforeLoads?.length > 0
         | 
| 20 | 
            +
                  return true
         | 
| 20 21 |  | 
| 21 22 | 
             
                flag = true
         | 
| 22 23 |  | 
| 23 24 | 
             
                for filter in @__beforeLoads
         | 
| 24 | 
            -
                   | 
| 25 | 
            -
             | 
| 25 | 
            +
                  unless Object.isFunction filter
         | 
| 26 | 
            +
                    filter = @[filter]
         | 
| 27 | 
            +
                  flag = flag && filter.apply @, opts
         | 
| 26 28 |  | 
| 27 29 | 
             
                return flag
         | 
| 28 30 |  | 
| 29 31 | 
             
              __runAfterLoads: (opts...) ->
         | 
| 30 | 
            -
                if @__afterLoads | 
| 32 | 
            +
                if @__afterLoads?.length > 0
         | 
| 31 33 | 
             
                  for filter in @__afterLoads
         | 
| 32 | 
            -
                     | 
| 33 | 
            -
             | 
| 34 | 
            +
                    unless Object.isFunction filter
         | 
| 35 | 
            +
                      filter = @[filter]
         | 
| 36 | 
            +
                    filter.apply @, opts
         | 
| 34 37 |  | 
| 35 38 | 
             
              __runAfterUnloads: (opts...) ->
         | 
| 36 | 
            -
                if @__afterUnloads | 
| 39 | 
            +
                if @__afterUnloads?.length > 0
         | 
| 37 40 | 
             
                  for filter in @__afterUnloads
         | 
| 38 | 
            -
                     | 
| 39 | 
            -
             | 
| 41 | 
            +
                    unless Object.isFunction filter
         | 
| 42 | 
            +
                      filter = @[filter]
         | 
| 43 | 
            +
                    filter.apply @, opts
         | 
| @@ -1,15 +1,18 @@ | |
| 1 1 | 
             
            Joosy.Modules.Log =
         | 
| 2 2 | 
             
              log: (args...) ->
         | 
| 3 | 
            -
                return  | 
| 3 | 
            +
                return unless console?
         | 
| 4 4 |  | 
| 5 5 | 
             
                if console.log.apply?
         | 
| 6 6 | 
             
                  args.unshift "Joosy>"
         | 
| 7 | 
            -
                  console.log | 
| 7 | 
            +
                  console.log args...
         | 
| 8 8 | 
             
                else
         | 
| 9 | 
            -
                  console.log | 
| 9 | 
            +
                  console.log args.first()
         | 
| 10 10 |  | 
| 11 11 | 
             
              debug: (args...) ->
         | 
| 12 | 
            -
                 | 
| 12 | 
            +
                return unless Joosy.debug
         | 
| 13 | 
            +
                @log args...
         | 
| 13 14 |  | 
| 14 15 | 
             
              debugAs: (context, string, args...) ->
         | 
| 15 | 
            -
                 | 
| 16 | 
            +
                return unless Joosy.debug
         | 
| 17 | 
            +
                context = Joosy.Module.__className(context) || 'unknown context'
         | 
| 18 | 
            +
                @debug "#{context}> #{string}", args...
         | 
| @@ -3,41 +3,51 @@ moduleKeywords = ['included', 'extended'] | |
| 3 3 | 
             
            class Joosy.Module
         | 
| 4 4 | 
             
              @__namespace__: []
         | 
| 5 5 |  | 
| 6 | 
            -
              @ | 
| 7 | 
            -
                 | 
| 6 | 
            +
              @__className = (klass) ->
         | 
| 7 | 
            +
                unless Object.isFunction(klass)
         | 
| 8 | 
            +
                  klass = klass.constructor
         | 
| 8 9 |  | 
| 9 10 | 
             
                if klass.name?
         | 
| 10 11 | 
             
                  klass.name
         | 
| 11 12 | 
             
                else
         | 
| 12 | 
            -
                  klass.toString().replace | 
| 13 | 
            +
                  klass.toString().replace /^function ([a-zA-Z]+)\([\s\S]+/, '$1'
         | 
| 13 14 |  | 
| 14 | 
            -
              @hasAncestor  | 
| 15 | 
            -
                 | 
| 15 | 
            +
              @hasAncestor: (what, klass) ->
         | 
| 16 | 
            +
                unless what? && klass?
         | 
| 17 | 
            +
                  return false
         | 
| 16 18 |  | 
| 17 | 
            -
                [ | 
| 19 | 
            +
                [what, klass] = [what.prototype, klass.prototype]
         | 
| 18 20 |  | 
| 19 21 | 
             
                while what
         | 
| 20 | 
            -
                   | 
| 22 | 
            +
                  if what == klass
         | 
| 23 | 
            +
                    return true
         | 
| 21 24 | 
             
                  what = what.constructor?.__super__
         | 
| 22 25 |  | 
| 23 26 | 
             
                false
         | 
| 24 | 
            -
             | 
| 25 | 
            -
              @ | 
| 26 | 
            -
                 | 
| 27 | 
            -
             | 
| 28 | 
            -
             | 
| 27 | 
            +
                
         | 
| 28 | 
            +
              @merge: (destination, source, unsafe=true) ->
         | 
| 29 | 
            +
                for key, value of source
         | 
| 30 | 
            +
                  if source.hasOwnProperty(key)
         | 
| 31 | 
            +
                    if unsafe || !destination.hasOwnProperty(key)
         | 
| 32 | 
            +
                      destination[key] = value
         | 
| 33 | 
            +
                destination
         | 
| 34 | 
            +
             | 
| 35 | 
            +
              @include: (object) ->
         | 
| 36 | 
            +
                unless object
         | 
| 37 | 
            +
                  throw new Error 'include(object) requires obj'
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                Object.each object, (key, value) =>
         | 
| 29 40 | 
             
                  if key not in moduleKeywords
         | 
| 30 41 | 
             
                    this::[key] = value
         | 
| 31 42 |  | 
| 32 | 
            -
                 | 
| 33 | 
            -
                 | 
| 43 | 
            +
                object.included?.apply this
         | 
| 44 | 
            +
                null
         | 
| 34 45 |  | 
| 35 | 
            -
              @extend: ( | 
| 36 | 
            -
                 | 
| 46 | 
            +
              @extend: (object) ->
         | 
| 47 | 
            +
                unless object
         | 
| 48 | 
            +
                  throw new Error 'extend(object) requires object'
         | 
| 37 49 |  | 
| 38 | 
            -
                 | 
| 39 | 
            -
                  if key not in moduleKeywords
         | 
| 40 | 
            -
                    this[key] = value
         | 
| 50 | 
            +
                @merge this, object
         | 
| 41 51 |  | 
| 42 | 
            -
                 | 
| 43 | 
            -
                 | 
| 52 | 
            +
                object.extended?.apply this
         | 
| 53 | 
            +
                null
         | 
| @@ -4,17 +4,20 @@ | |
| 4 4 | 
             
            Joosy.Modules.Renderer =
         | 
| 5 5 |  | 
| 6 6 | 
             
              __renderer: ->
         | 
| 7 | 
            -
                throw new Error "#{@constructor | 
| 7 | 
            +
                throw new Error "#{Joosy.Module.__className @constructor} does not have an attached template"
         | 
| 8 8 |  | 
| 9 9 | 
             
              __helpers: null
         | 
| 10 10 |  | 
| 11 11 | 
             
              included: ->
         | 
| 12 | 
            -
                @view = (template) ->
         | 
| 13 | 
            -
                  if Object.isFunction | 
| 12 | 
            +
                @view = (template, options={}) ->
         | 
| 13 | 
            +
                  if Object.isFunction template
         | 
| 14 14 | 
             
                    @::__renderer = template
         | 
| 15 15 | 
             
                  else
         | 
| 16 16 | 
             
                    @::__renderer = (locals={}) ->
         | 
| 17 | 
            -
                       | 
| 17 | 
            +
                      if options.dynamic
         | 
| 18 | 
            +
                        @renderDynamic template, locals
         | 
| 19 | 
            +
                      else
         | 
| 20 | 
            +
                        @render template, locals
         | 
| 18 21 |  | 
| 19 22 | 
             
                @helpers = (helpers...) ->
         | 
| 20 23 | 
             
                  @::__helpers ||= []
         | 
| @@ -30,26 +33,23 @@ Joosy.Modules.Renderer = | |
| 30 33 | 
             
              __instantiateHelpers: ->
         | 
| 31 34 | 
             
                unless @__helpersInstance
         | 
| 32 35 | 
             
                  @__helpersInstance = Object.extended Joosy.Helpers.Application
         | 
| 33 | 
            -
             | 
| 34 | 
            -
                  @__helpersInstance.render = =>
         | 
| 35 | 
            -
                    @render(arguments...)
         | 
| 36 36 |  | 
| 37 37 | 
             
                  @__helpersInstance.widget = (element, widget) =>
         | 
| 38 38 | 
             
                    @widgets ||= {}
         | 
| 39 39 |  | 
| 40 40 | 
             
                    uuid    = Joosy.uuid()
         | 
| 41 | 
            -
                    element = document.createElement | 
| 42 | 
            -
                    temp    = document.createElement | 
| 41 | 
            +
                    element = document.createElement element
         | 
| 42 | 
            +
                    temp    = document.createElement 'div'
         | 
| 43 43 |  | 
| 44 | 
            -
                    element.id | 
| 44 | 
            +
                    element.id = uuid
         | 
| 45 45 | 
             
                    @widgets['#'+uuid] = widget
         | 
| 46 46 |  | 
| 47 | 
            -
                    temp.appendChild | 
| 47 | 
            +
                    temp.appendChild element
         | 
| 48 48 | 
             
                    temp.innerHTML
         | 
| 49 49 |  | 
| 50 50 | 
             
                  if @__helpers
         | 
| 51 51 | 
             
                    for helper in @__helpers
         | 
| 52 | 
            -
                       | 
| 52 | 
            +
                      Joosy.Module.merge @__helpersInstance, helper
         | 
| 53 53 |  | 
| 54 54 | 
             
                @__helpersInstance
         | 
| 55 55 |  | 
| @@ -57,60 +57,123 @@ Joosy.Modules.Renderer = | |
| 57 57 | 
             
              __proxifyHelpers: (locals) ->
         | 
| 58 58 | 
             
                if locals.hasOwnProperty '__proto__'
         | 
| 59 59 | 
             
                  locals.__proto__ = @__instantiateHelpers()
         | 
| 60 | 
            -
             | 
| 61 60 | 
             
                  locals
         | 
| 62 61 | 
             
                else
         | 
| 63 62 | 
             
                  unless @__helpersProxyInstance
         | 
| 64 63 | 
             
                    @__helpersProxyInstance = (locals) ->
         | 
| 65 | 
            -
                       | 
| 64 | 
            +
                      Joosy.Module.merge this, locals
         | 
| 66 65 |  | 
| 67 66 | 
             
                    @__helpersProxyInstance.prototype = @__instantiateHelpers()
         | 
| 68 67 |  | 
| 69 | 
            -
                  new @__helpersProxyInstance | 
| 68 | 
            +
                  new @__helpersProxyInstance locals
         | 
| 70 69 |  | 
| 71 | 
            -
              render: (template, locals={}) ->
         | 
| 72 | 
            -
                 | 
| 70 | 
            +
              render: (template, locals={}, parentStackPointer=false) ->
         | 
| 71 | 
            +
                @__render false, template, locals, parentStackPointer
         | 
| 72 | 
            +
                
         | 
| 73 | 
            +
              renderDynamic: (template, locals={}, parentStackPointer=false) ->
         | 
| 74 | 
            +
                @__render true, template, locals, parentStackPointer
         | 
| 75 | 
            +
             | 
| 76 | 
            +
              __render: (dynamic, template, locals={}, parentStackPointer=false) ->
         | 
| 77 | 
            +
                stack = @__renderingStackChildFor parentStackPointer
         | 
| 78 | 
            +
                
         | 
| 79 | 
            +
                stack.template = template
         | 
| 80 | 
            +
                
         | 
| 81 | 
            +
                isResource   = Joosy.Module.hasAncestor locals.constructor, Joosy.Resource.Generic
         | 
| 82 | 
            +
                isCollection = Joosy.Module.hasAncestor locals.constructor, Joosy.Resource.Collection
         | 
| 73 83 |  | 
| 74 84 | 
             
                if Object.isString template
         | 
| 75 85 | 
             
                  if @__renderSection?
         | 
| 76 86 | 
             
                    template = Joosy.Application.templater.resolveTemplate @__renderSection(), template, this
         | 
| 77 87 |  | 
| 78 88 | 
             
                  template = Joosy.Application.templater.buildView template
         | 
| 79 | 
            -
                else if !Object.isFunction | 
| 80 | 
            -
                  throw new Error "#{Joosy.Module. | 
| 81 | 
            -
             | 
| 82 | 
            -
                if !Object.isObject(locals) && !isResource
         | 
| 83 | 
            -
                  throw new Error "#{Joosy.Module. | 
| 84 | 
            -
             | 
| 85 | 
            -
                 | 
| 86 | 
            -
             | 
| 87 | 
            -
                if !isResource
         | 
| 88 | 
            -
                  locals = @__proxifyHelpers(locals)
         | 
| 89 | 
            -
                  morph  = Metamorph template(locals)
         | 
| 90 | 
            -
                  update = => morph.html template(locals)
         | 
| 89 | 
            +
                else if !Object.isFunction template
         | 
| 90 | 
            +
                  throw new Error "#{Joosy.Module.__className @}> template (maybe @view) does not look like a string or lambda"
         | 
| 91 | 
            +
                
         | 
| 92 | 
            +
                if !Object.isObject(locals) && !isResource && !isCollection
         | 
| 93 | 
            +
                  throw new Error "#{Joosy.Module.__className @}> locals (maybe @data?) not in: dumb hash, Resource, Collection"
         | 
| 94 | 
            +
             | 
| 95 | 
            +
                if isResource
         | 
| 96 | 
            +
                  stack.locals = locals.e
         | 
| 91 97 | 
             
                else
         | 
| 92 | 
            -
                  locals | 
| 93 | 
            -
                  morph    = Metamorph template(locals.e)
         | 
| 94 | 
            -
                  update   = => morph.html template(locals.e)
         | 
| 98 | 
            +
                  stack.locals = locals
         | 
| 95 99 |  | 
| 96 | 
            -
                 | 
| 97 | 
            -
             | 
| 98 | 
            -
             | 
| 99 | 
            -
             | 
| 100 | 
            -
             | 
| 100 | 
            +
                renderers =
         | 
| 101 | 
            +
                  render: (template, locals={}) =>
         | 
| 102 | 
            +
                    @render template, locals, stack
         | 
| 103 | 
            +
                  renderDynamic: (template, locals={}) =>
         | 
| 104 | 
            +
                    @renderDynamic template, locals, stack
         | 
| 105 | 
            +
                    
         | 
| 106 | 
            +
                context = =>
         | 
| 107 | 
            +
                  data = {}
         | 
| 108 | 
            +
                  Joosy.Module.merge data, stack.locals
         | 
| 109 | 
            +
                  Joosy.Module.merge data, @__instantiateHelpers(), false
         | 
| 110 | 
            +
                  Joosy.Module.merge data, renderers
         | 
| 111 | 
            +
                  data
         | 
| 112 | 
            +
                
         | 
| 113 | 
            +
                if dynamic
         | 
| 114 | 
            +
                  morph  = Metamorph template(context())
         | 
| 115 | 
            +
                  update = =>
         | 
| 116 | 
            +
                    for child in stack.children
         | 
| 117 | 
            +
                      @__removeMetamorphs child
         | 
| 118 | 
            +
                    stack.children = []
         | 
| 119 | 
            +
                    morph.html template(context())
         | 
| 120 | 
            +
                    @refreshElements?()
         | 
| 121 | 
            +
             | 
| 122 | 
            +
                  # This is here to break stack tree and save from 
         | 
| 123 | 
            +
                  # repeating DOM handling
         | 
| 124 | 
            +
                  update = update.debounce 0
         | 
| 125 | 
            +
             | 
| 126 | 
            +
                  if isCollection
         | 
| 127 | 
            +
                    for resource in locals.data
         | 
| 128 | 
            +
                      resource.bind 'changed', update
         | 
| 129 | 
            +
                      stack.metamorphBindings.push [resource, update]
         | 
| 130 | 
            +
                  if isResource || isCollection
         | 
| 131 | 
            +
                    locals.bind 'changed', update
         | 
| 132 | 
            +
                    stack.metamorphBindings.push [locals, update]
         | 
| 133 | 
            +
                  else
         | 
| 134 | 
            +
                    for key, object of locals
         | 
| 135 | 
            +
                      if locals.hasOwnProperty key
         | 
| 136 | 
            +
                        if object?.bind? && object?.unbind?
         | 
| 137 | 
            +
                          object.bind 'changed', update
         | 
| 138 | 
            +
                          stack.metamorphBindings.push [object, update]
         | 
| 101 139 |  | 
| 102 | 
            -
             | 
| 103 | 
            -
             | 
| 140 | 
            +
                  morph.outerHTML()
         | 
| 141 | 
            +
                else
         | 
| 142 | 
            +
                  template context()
         | 
| 143 | 
            +
             | 
| 144 | 
            +
              __renderingStackElement: (parent=null) ->
         | 
| 145 | 
            +
                metamorphBindings: []
         | 
| 146 | 
            +
                locals: null
         | 
| 147 | 
            +
                template: null
         | 
| 148 | 
            +
                children: []
         | 
| 149 | 
            +
                parent: parent
         | 
| 150 | 
            +
             | 
| 151 | 
            +
              __renderingStackChildFor: (parentPointer) ->
         | 
| 152 | 
            +
                if !@__renderingStack
         | 
| 153 | 
            +
                  @__renderingStack = []
         | 
| 154 | 
            +
                
         | 
| 155 | 
            +
                if !parentPointer
         | 
| 156 | 
            +
                  element = @__renderingStackElement()
         | 
| 157 | 
            +
                  @__renderingStack.push element
         | 
| 158 | 
            +
                  element
         | 
| 159 | 
            +
                else
         | 
| 160 | 
            +
                  element = @__renderingStackElement parentPointer
         | 
| 161 | 
            +
                  parentPointer.children.push element
         | 
| 162 | 
            +
                  element
         | 
| 163 | 
            +
             | 
| 164 | 
            +
              __removeMetamorphs: (stackPointer=false) ->
         | 
| 165 | 
            +
                remove = (stackPointer) =>
         | 
| 166 | 
            +
                  if stackPointer?.children
         | 
| 167 | 
            +
                    for child in stackPointer.children
         | 
| 168 | 
            +
                      @__removeMetamorphs child
         | 
| 169 | 
            +
              
         | 
| 170 | 
            +
                  if stackPointer?.metamorphBindings
         | 
| 171 | 
            +
                    for [object, callback] in stackPointer.metamorphBindings
         | 
| 172 | 
            +
                      object.unbind callback
         | 
| 173 | 
            +
                    stackPointer.metamorphBindings = []
         | 
| 174 | 
            +
                
         | 
| 175 | 
            +
                unless stackPointer
         | 
| 176 | 
            +
                  @__renderingStack?.each (stackPointer) ->
         | 
| 177 | 
            +
                    remove stackPointer
         | 
| 104 178 | 
             
                else
         | 
| 105 | 
            -
                   | 
| 106 | 
            -
                    if locals.hasOwnProperty key
         | 
| 107 | 
            -
                      if object?.bind? && object?.unbind?
         | 
| 108 | 
            -
                        object.bind 'changed', update
         | 
| 109 | 
            -
                        @__metamorphs.push [object, update]
         | 
| 110 | 
            -
             | 
| 111 | 
            -
                morph.outerHTML()
         | 
| 112 | 
            -
             | 
| 113 | 
            -
              __removeMetamorphs: ->
         | 
| 114 | 
            -
                if @__metamorphs
         | 
| 115 | 
            -
                  for [object, callback] in @__metamorphs
         | 
| 116 | 
            -
                    object.unbind callback
         | 
| 179 | 
            +
                  remove stackPointer
         | 
| @@ -15,11 +15,11 @@ Joosy.Modules.TimeManager = | |
| 15 15 |  | 
| 16 16 | 
             
                timer
         | 
| 17 17 |  | 
| 18 | 
            -
               | 
| 18 | 
            +
              __clearTime: ->
         | 
| 19 19 | 
             
                if @__intervals
         | 
| 20 20 | 
             
                  for entry in @__intervals
         | 
| 21 21 | 
             
                    window.clearInterval entry
         | 
| 22 22 |  | 
| 23 23 | 
             
                if @__timeouts
         | 
| 24 24 | 
             
                  for entry in @__timeouts
         | 
| 25 | 
            -
                    window.clearTimeout entry
         | 
| 25 | 
            +
                    window.clearTimeout entry
         | 
| @@ -1,6 +1,6 @@ | |
| 1 1 | 
             
            Joosy.Modules.WidgetsManager =
         | 
| 2 2 | 
             
              registerWidget: (container, widget) ->
         | 
| 3 | 
            -
                if Joosy.Module.hasAncestor | 
| 3 | 
            +
                if Joosy.Module.hasAncestor widget, Joosy.Widget
         | 
| 4 4 | 
             
                  widget = new widget()
         | 
| 5 5 |  | 
| 6 6 | 
             
                @__activeWidgets ||= []
         | 
| @@ -11,14 +11,14 @@ Joosy.Modules.WidgetsManager = | |
| 11 11 | 
             
              unregisterWidget: (widget) ->
         | 
| 12 12 | 
             
                widget.__unload()
         | 
| 13 13 |  | 
| 14 | 
            -
                @__activeWidgets.splice | 
| 14 | 
            +
                @__activeWidgets.splice @__activeWidgets.indexOf(widget), 1
         | 
| 15 15 |  | 
| 16 16 | 
             
              __collectWidgets: ->
         | 
| 17 | 
            -
                widgets = Object.extended | 
| 17 | 
            +
                widgets = Object.extended @widgets || {}
         | 
| 18 18 |  | 
| 19 19 | 
             
                klass = this
         | 
| 20 20 | 
             
                while klass = klass.constructor.__super__
         | 
| 21 | 
            -
                   | 
| 21 | 
            +
                  Joosy.Module.merge widgets, klass.widgets, false
         | 
| 22 22 |  | 
| 23 23 | 
             
                widgets
         | 
| 24 24 |  | 
| @@ -30,7 +30,7 @@ Joosy.Modules.WidgetsManager = | |
| 30 30 | 
             
                  if selector == '$container'
         | 
| 31 31 | 
             
                    activeSelector = @container
         | 
| 32 32 | 
             
                  else
         | 
| 33 | 
            -
                    if r = selector.match | 
| 33 | 
            +
                    if r = selector.match /\$([A-z_]+)/
         | 
| 34 34 | 
             
                      selector = @elements[r[1]]
         | 
| 35 35 |  | 
| 36 36 | 
             
                    activeSelector = $(selector, @container)
         | 
| @@ -38,15 +38,15 @@ Joosy.Modules.WidgetsManager = | |
| 38 38 | 
             
                  registered[selector] = Object.extended()
         | 
| 39 39 |  | 
| 40 40 | 
             
                  activeSelector.each (index, elem) =>
         | 
| 41 | 
            -
                    if Joosy.Module.hasAncestor | 
| 41 | 
            +
                    if Joosy.Module.hasAncestor widget, Joosy.Widget
         | 
| 42 42 | 
             
                      instance = new widget
         | 
| 43 43 | 
             
                    else
         | 
| 44 44 | 
             
                      instance = widget.call this, index
         | 
| 45 45 |  | 
| 46 | 
            -
                    registered[selector][Joosy.Module. | 
| 47 | 
            -
                    registered[selector][Joosy.Module. | 
| 46 | 
            +
                    registered[selector][Joosy.Module.__className instance] ||= 0
         | 
| 47 | 
            +
                    registered[selector][Joosy.Module.__className instance]  += 1
         | 
| 48 48 |  | 
| 49 | 
            -
                    @registerWidget | 
| 49 | 
            +
                    @registerWidget $(elem), instance
         | 
| 50 50 |  | 
| 51 51 | 
             
                registered.each (selector, value) =>
         | 
| 52 52 | 
             
                  value.each (widget, count) =>
         | 
| @@ -55,4 +55,4 @@ Joosy.Modules.WidgetsManager = | |
| 55 55 | 
             
              __unloadWidgets: ->
         | 
| 56 56 | 
             
                if @__activeWidgets
         | 
| 57 57 | 
             
                  for widget in @__activeWidgets
         | 
| 58 | 
            -
                    widget.__unload()
         | 
| 58 | 
            +
                    widget.__unload()
         | 
| @@ -33,21 +33,26 @@ class Joosy.Page extends Joosy.Module | |
| 33 33 | 
             
              @layout: (layoutClass) ->
         | 
| 34 34 | 
             
                @::__layoutClass = layoutClass
         | 
| 35 35 |  | 
| 36 | 
            -
              @beforePaint: (callback) -> | 
| 37 | 
            -
             | 
| 38 | 
            -
              @ | 
| 39 | 
            -
             | 
| 36 | 
            +
              @beforePaint: (callback) ->
         | 
| 37 | 
            +
                @::__beforePaint = callback
         | 
| 38 | 
            +
              @paint: (callback) ->
         | 
| 39 | 
            +
                @::__paint = callback
         | 
| 40 | 
            +
              @afterPaint: (callback) ->
         | 
| 41 | 
            +
                @::__afterPaint = callback
         | 
| 42 | 
            +
              @erase: (callback) ->
         | 
| 43 | 
            +
                @::__erase = callback
         | 
| 40 44 |  | 
| 41 45 | 
             
              constructor: (@params, @previous) ->
         | 
| 42 46 | 
             
                @__layoutClass ||= ApplicationLayout
         | 
| 43 47 |  | 
| 44 | 
            -
                if @__runBeforeLoads | 
| 48 | 
            +
                if @__runBeforeLoads @params, @previous
         | 
| 45 49 | 
             
                  if !@previous?.layout?.uuid? || @previous?.__layoutClass != @__layoutClass
         | 
| 46 50 | 
             
                    @__bootstrapLayout()
         | 
| 47 51 | 
             
                  else
         | 
| 48 52 | 
             
                    @__bootstrap()
         | 
| 49 53 |  | 
| 50 | 
            -
              navigate: (args...) -> | 
| 54 | 
            +
              navigate: (args...) ->
         | 
| 55 | 
            +
                Joosy.Router.navigate(args...)
         | 
| 51 56 |  | 
| 52 57 | 
             
              __renderSection: ->
         | 
| 53 58 | 
             
                'pages'
         | 
| @@ -62,23 +67,23 @@ class Joosy.Page extends Joosy.Module | |
| 62 67 | 
             
                @refreshElements()
         | 
| 63 68 | 
             
                @__delegateEvents()
         | 
| 64 69 | 
             
                @__setupWidgets()
         | 
| 65 | 
            -
                @__runAfterLoads | 
| 70 | 
            +
                @__runAfterLoads @params, @previous
         | 
| 66 71 | 
             
                if @__scrollElement
         | 
| 67 | 
            -
                  scroll = $(@__extractSelector | 
| 68 | 
            -
                  Joosy.Modules.Log.debugAs @, "Scrolling to #{@__extractSelector | 
| 72 | 
            +
                  scroll = $(@__extractSelector @__scrollElement).offset()?.top + @__scrollMargin
         | 
| 73 | 
            +
                  Joosy.Modules.Log.debugAs @, "Scrolling to #{@__extractSelector @__scrollElement}"
         | 
| 69 74 | 
             
                  $('html, body').animate {scrollTop: scroll}, @__scrollSpeed, =>
         | 
| 70 | 
            -
                     | 
| 75 | 
            +
                    if @__scrollSpeed != 0
         | 
| 76 | 
            +
                      @__releaseHeight()
         | 
| 71 77 |  | 
| 72 78 | 
             
                Joosy.Modules.Log.debugAs @, "Page loaded"
         | 
| 73 79 |  | 
| 74 80 | 
             
              __unload: ->
         | 
| 75 | 
            -
                @ | 
| 81 | 
            +
                @__clearTime()
         | 
| 76 82 | 
             
                @__unloadWidgets()
         | 
| 77 83 | 
             
                @__removeMetamorphs()
         | 
| 78 | 
            -
                @__runAfterUnloads | 
| 84 | 
            +
                @__runAfterUnloads @params, @previous
         | 
| 79 85 | 
             
                delete @previous
         | 
| 80 86 |  | 
| 81 | 
            -
             | 
| 82 87 | 
             
              __callSyncedThrough: (entity, receiver, params, callback) ->
         | 
| 83 88 | 
             
                if entity?[receiver]?
         | 
| 84 89 | 
             
                  entity[receiver].apply entity, params.clone().add(callback)
         | 
| @@ -99,7 +104,8 @@ class Joosy.Page extends Joosy.Module | |
| 99 104 |  | 
| 100 105 | 
             
                callbacksParams = [@layout.content()]
         | 
| 101 106 |  | 
| 102 | 
            -
                 | 
| 107 | 
            +
                if @__scrollElement && @__scrollSpeed != 0
         | 
| 108 | 
            +
                 @__fixHeight()
         | 
| 103 109 |  | 
| 104 110 | 
             
                @wait "stageClear dataReceived", =>
         | 
| 105 111 | 
             
                  @__callSyncedThrough this, '__paint', callbacksParams, =>
         | 
| @@ -123,17 +129,20 @@ class Joosy.Page extends Joosy.Module | |
| 123 129 |  | 
| 124 130 | 
             
              __bootstrapLayout: ->
         | 
| 125 131 | 
             
                Joosy.Modules.Log.debugAs @, "Boostraping page with layout"
         | 
| 126 | 
            -
                @layout = new @__layoutClass
         | 
| 132 | 
            +
                @layout = new @__layoutClass(@params)
         | 
| 127 133 |  | 
| 128 134 | 
             
                callbacksParams = [Joosy.Application.content(), this]
         | 
| 129 135 |  | 
| 130 | 
            -
                 | 
| 136 | 
            +
                if @__scrollElement && @__scrollSpeed != 0
         | 
| 137 | 
            +
                  @__fixHeight()
         | 
| 131 138 |  | 
| 132 139 | 
             
                @wait "stageClear dataReceived", =>
         | 
| 133 140 | 
             
                  @__callSyncedThrough @layout, '__paint', callbacksParams, =>
         | 
| 134 141 | 
             
                    # Layout HTML
         | 
| 135 | 
            -
                     | 
| 136 | 
            -
             | 
| 142 | 
            +
                    data = Joosy.Module.merge {}, @layout.data || {}
         | 
| 143 | 
            +
                    data = Joosy.Module.merge data, yield: => @layout.yield()
         | 
| 144 | 
            +
                    
         | 
| 145 | 
            +
                    @swapContainer Joosy.Application.content(), @layout.__renderer data
         | 
| 137 146 |  | 
| 138 147 | 
             
                    # Page HTML
         | 
| 139 148 | 
             
                    @swapContainer @layout.content(), @__renderer(@data || {})
         | 
| @@ -151,6 +160,7 @@ class Joosy.Page extends Joosy.Module | |
| 151 160 | 
             
                  @__callSyncedThrough @layout, '__beforePaint', callbacksParams, =>
         | 
| 152 161 | 
             
                    @trigger 'stageClear'
         | 
| 153 162 |  | 
| 154 | 
            -
                @__callSyncedThrough  | 
| 155 | 
            -
                   | 
| 156 | 
            -
             | 
| 163 | 
            +
                @__callSyncedThrough @layout, '__fetch', [], =>
         | 
| 164 | 
            +
                  @__callSyncedThrough this, '__fetch', [], =>
         | 
| 165 | 
            +
                    Joosy.Modules.Log.debugAs @, "Fetch complete"
         | 
| 166 | 
            +
                    @trigger 'dataReceived'
         |