diff --git a/src/chaplin/views/collection_view.coffee b/src/chaplin/views/collection_view.coffee index 7b42b08d..c8b3038c 100644 --- a/src/chaplin/views/collection_view.coffee +++ b/src/chaplin/views/collection_view.coffee @@ -5,20 +5,16 @@ Backbone = require 'backbone' View = require 'chaplin/views/view' utils = require 'chaplin/lib/utils' -# Shortcut to access the DOM manipulation library. -$ = Backbone.$ - filterChildren = (nodeList, selector) -> return nodeList unless selector for node in nodeList when Backbone.utils.matchesSelector node, selector node -toggleElement = do -> - if $ - (elem, visible) -> $(elem).toggle visible +toggleElement = (elem, visible) -> + if Backbone.$ + $(elem).toggle visible else - (elem, visible) -> - elem.style.display = (if visible then '' else 'none') + elem.style.display = (if visible then '' else 'none') startAnimation = (elem, useCssAnimation, cls) -> if useCssAnimation @@ -26,68 +22,40 @@ startAnimation = (elem, useCssAnimation, cls) -> else elem.style.opacity = 0 -endAnimation = do -> - if $ - (elem, duration) -> elem.animate {opacity: 1}, duration - else - (elem, duration) -> - elem.style.transition = "opacity #{(duration / 1000)}s" - elem.style.opacity = 1 - -insertView = do -> - if $ - (list, viewEl, position, length, itemSelector) -> - $list = $ list - insertInMiddle = (0 < position < length) - isEnd = (length) -> length is 0 or position is length - - if insertInMiddle or itemSelector - # Get the children which originate from item views. - children = $list.children itemSelector - childrenLength = children.length - - # Check if it needs to be inserted. - unless children[position] is viewEl - if isEnd childrenLength - # Insert at the end. - $list.append viewEl - else - # Insert at the right position. - if position is 0 - children.eq(position).before viewEl - else - children.eq(position - 1).after viewEl - else - method = if isEnd length then 'append' else 'prepend' - $list[method] viewEl +endAnimation = (elem, duration) -> + if Backbone.$ + elem.animate {opacity: 1}, duration else - (list, viewEl, position, length, itemSelector) -> - insertInMiddle = (0 < position < length) - isEnd = (length) -> length is 0 or position is length - - if insertInMiddle or itemSelector - # Get the children which originate from item views. - children = filterChildren list.children, itemSelector - childrenLength = children.length - - # Check if it needs to be inserted. - unless children[position] is viewEl - if isEnd childrenLength - # Insert at the end. - list.appendChild viewEl - else if position is 0 - # Insert at the right position. - list.insertBefore viewEl, children[position] - else - last = children[position - 1] - if list.lastChild is last - list.appendChild viewEl - else - list.insertBefore viewEl, last.nextElementSibling - else if isEnd length + elem.style.transition = "opacity #{(duration / 1000)}s" + elem.opacity = 1 + +insertView = (list, viewEl, position, length, itemSelector) -> + insertInMiddle = (0 < position < length) + isEnd = (length) -> length is 0 or position is length + + if insertInMiddle or itemSelector + # Get the children which originate from item views. + children = filterChildren list.children, itemSelector + childrenLength = children.length + + # Check if it needs to be inserted. + unless children[position] is viewEl + if isEnd childrenLength + # Insert at the end. list.appendChild viewEl + else if position is 0 + # Insert at the right position. + list.insertBefore viewEl, children[position] else - list.insertBefore viewEl, list.firstChild + last = children[position - 1] + if list.lastChild is last + list.appendChild viewEl + else + list.insertBefore viewEl, last.nextElementSibling + else if isEnd length + list.appendChild viewEl + else + list.insertBefore viewEl, list.firstChild # General class for rendering Collections. # Derive this class and declare at least `itemView` or override @@ -163,7 +131,7 @@ module.exports = class CollectionView extends View # A function that will be executed after each filter. # Hides excluded items by default. filterCallback: (view, included) -> - view.$el.stop(true, true) if $ + view.$el.stop(true, true) if Backbone.$ toggleElement view.el, included # View lists diff --git a/src/chaplin/views/layout.coffee b/src/chaplin/views/layout.coffee index 76b179e7..e21c81e0 100644 --- a/src/chaplin/views/layout.coffee +++ b/src/chaplin/views/layout.coffee @@ -7,9 +7,6 @@ utils = require 'chaplin/lib/utils' EventBroker = require 'chaplin/lib/event_broker' View = require 'chaplin/views/view' -# Shortcut to access the DOM manipulation library. -$ = Backbone.$ - module.exports = class Layout extends View # Bind to document body by default. el: 'body' @@ -100,7 +97,7 @@ module.exports = class Layout extends View openLink: (event) => return if utils.modifierKeyPressed(event) - el = if $ then event.currentTarget else event.delegateTarget + el = if Backbone.$ then event.currentTarget else event.delegateTarget isAnchor = el.nodeName is 'A' # Get the href and perform checks on it. @@ -118,7 +115,7 @@ module.exports = class Layout extends View skipRouting = @settings.skipRouting type = typeof skipRouting return if type is 'function' and not skipRouting(href, el) or - type is 'string' and (if $ then $(el).is(skipRouting) else Backbone.utils.matchesSelector el, skipRouting) + type is 'string' and (if Backbone.$ then Backbone.$(el).is(skipRouting) else el.matches skipRouting) # Handle external links. external = isAnchor and @isExternalLink el @@ -206,14 +203,14 @@ module.exports = class Layout extends View # Apply the region selector. instance.container = if region.selector is '' - if $ + if Backbone.$ region.instance.$el else region.instance.el else if region.instance.noWrap - if $ - $(region.instance.container).find region.selector + if Backbone.$ + Backbone.$(region.instance.container).find region.selector else region.instance.container.querySelector region.selector else diff --git a/src/chaplin/views/view.coffee b/src/chaplin/views/view.coffee index 6890dc8d..54037cf1 100644 --- a/src/chaplin/views/view.coffee +++ b/src/chaplin/views/view.coffee @@ -6,42 +6,6 @@ mediator = require 'chaplin/mediator' EventBroker = require 'chaplin/lib/event_broker' utils = require 'chaplin/lib/utils' -# Shortcut to access the DOM manipulation library. -$ = Backbone.$ - -# Function bind shortcut. -bind = do -> - if Function::bind - (item, ctx) -> item.bind ctx - else if _.bind - _.bind - -setHTML = do -> - if $ - (elem, html) -> $(elem).html html - else - (elem, html) -> elem.innerHTML = html - -attach = do -> - if $ - (view) -> - actual = $(view.container) - if typeof view.containerMethod is 'function' - view.containerMethod actual, view.el - else - actual[view.containerMethod] view.el - else - (view) -> - actual = if typeof view.container is 'string' - document.querySelector view.container - else - view.container - - if typeof view.containerMethod is 'function' - view.containerMethod actual, view.el - else - actual[view.containerMethod] view.el - module.exports = class View extends Backbone.View # Mixin an EventBroker. _.extend @prototype, EventBroker @@ -69,7 +33,7 @@ module.exports = class View extends Backbone.View # Method which is used for adding the view to the DOM # Like jQuery’s `html`, `prepend`, `append`, `after`, `before` etc. - containerMethod: if $ then 'append' else 'appendChild' + containerMethod: if Backbone.$ then 'append' else 'appendChild' # Regions # ------- @@ -152,7 +116,7 @@ module.exports = class View extends Backbone.View @el = if region.instance.container? if region.instance.region? - $(region.instance.container).find region.selector + Backbone.$(region.instance.container).find region.selector else region.instance.container else @@ -193,7 +157,7 @@ module.exports = class View extends Backbone.View match = key.match /^(\S+)\s*(.*)$/ eventName = "#{match[1]}.delegateEvents#{@cid}" selector = match[2] - bound = bind handler, this + bound = _.bind handler, this @delegate eventName, (selector or null), bound return @@ -367,11 +331,16 @@ module.exports = class View extends Backbone.View # Delegate events to the top-level container in the template. @setElement el.firstChild, true else - setHTML @el, html + @setHTML html # Return the view. this + # Set the innerHTML of the view's `el`. Override this method to support + # older browsers (IE needs the html empty before, e.g.) + setHTML: (html) -> + @el.innerHTML = html + # This method is called after a specific `render` of a derived class. attach: -> # Attempt to bind this view to its named region. @@ -379,8 +348,25 @@ module.exports = class View extends Backbone.View # Automatically append to DOM if the container element is set. if @container and not document.body.contains @el - attach this - # Trigger an event. + if Backbone.$ + actual = Backbone.$(this.container) + if typeof this.containerMethod is 'function' + this.containerMethod actual, this.el + else + actual[this.containerMethod] this.el + else + actual = if typeof this.container is 'string' + document.querySelector this.container + else + this.container + + if typeof this.containerMethod is 'function' + this.containerMethod actual, this.el + else + actual[this.containerMethod] this.el + + # Hook into this event for things that require the view to be in the DOM + # (like calculating height / width or position). @trigger 'addedToDOM' # Disposal diff --git a/test/initialize.js b/test/initialize.js index e39e8741..3c9ecae9 100644 --- a/test/initialize.js +++ b/test/initialize.js @@ -9,19 +9,24 @@ window.clearInterval = timers.clearInterval; var paths = {}; var componentsFolder = 'bower_components'; -var match = window.location.search.match(/type=([-\w]+)/); +var match = window.location.search.match(/type=([-\w]+)&useDeps=([-\w]+)/); var testType = window.testType || (match ? match[1] : 'backbone'); +var useDeps = window.useDeps || (match ? match[2] : true); var addDeps = function() { - paths.underscore = '../' + componentsFolder + '/lodash/lodash.compat'; - paths.jquery = '../' + componentsFolder + '/jquery/jquery'; + if (useDeps) { + paths.underscore = '../' + componentsFolder + '/lodash/lodash.compat'; + paths.jquery = '../' + componentsFolder + '/jquery/jquery'; + } else { + paths.NativeView = '../' + componentsFolder + '/Backbone.NativeView/backbone.nativeview'; + } }; if (testType === 'backbone') { - paths.backbone = '../' + componentsFolder + '/backbone/backbone' - addDeps() + paths.backbone = '../' + componentsFolder + '/backbone/backbone'; + addDeps(); } else { - if (testType === 'deps') addDeps(); - paths.backbone = '../' + componentsFolder + '/exoskeleton/exoskeleton' + addDeps(); + paths.backbone = '../' + componentsFolder + '/exoskeleton/exoskeleton'; } var config = { @@ -46,7 +51,11 @@ if (testType === 'backbone' || testType === 'deps') { requirejs.config(config); if (testType === 'exos') { define('jquery', function(){}); - define('underscore', ['backbone'], function(Backbone){return Backbone.utils;}); + define('underscore', ['backbone'], function(Backbone){ + var _ = Backbone.utils; + _.bind = function(fn, ctx) { return fn.bind(ctx); } + return _; + }); } mocha.setup({ui: 'bdd', ignoreLeaks: true}); // Wonderful hack to send a message to grunt from inside a mocha test. @@ -86,16 +95,26 @@ window.addEventListener('DOMContentLoaded', function() { 'view', 'utils', 'sync_machine' - ]; - var loaded = []; - for (var i = 0, l = specs.length; i < l; i++) { - loaded.push(specs[i] + '_spec'); - } - require(loaded, function() { + ].map(function(file) { + return file + '_spec'; + }); + + var run = function() { if (window.mochaPhantomJS) { mochaPhantomJS.run(); } else { mocha.run(); } + }; + + require(specs, function() { + if (useDeps) { + run(); + } else { + require(['backbone', 'NativeView'], function(Backbone, NativeView) { + Backbone.View = NativeView; + run(); + }); + } }); }, false); diff --git a/test/spec/view_spec.coffee b/test/spec/view_spec.coffee index ed1a0454..adcdd3c1 100644 --- a/test/spec/view_spec.coffee +++ b/test/spec/view_spec.coffee @@ -224,26 +224,22 @@ define [ expect(view.undelegate).to.be.a 'function' spy = sinon.spy() - handler = view.delegate 'click', spy - expect(handler).to.be.a 'function' + view.delegate 'click', spy view.render() window.clickOnElement view.el expect(spy).was.called() - view.undelegate() + view.undelegateEvents() window.clickOnElement view.el expect(spy.callCount).to.be 1 spy = sinon.spy() - handler = view.delegate 'click', 'p', spy - expect(handler).to.be.a 'function' + view.delegate 'click', 'p', spy p = view.el.querySelector('p') window.clickOnElement p expect(spy).was.called() - expect(-> view.delegate spy).to.throwError() - - view.undelegate() + view.undelegateEvents() window.clickOnElement p expect(spy.callCount).to.be 1