From a3d62fdf694c29fefa8fc6183985e6b769757dc3 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Thu, 6 Jun 2013 01:24:52 -0400 Subject: [PATCH] Temporarily roll back ember rc5. We identified some things we need to fix. --- Gemfile | 6 +- Gemfile.lock | 29 +- app/assets/javascripts/application.js.erb | 2 +- .../external/handlebars-1.0.rc.3.js | 2202 ++++++++++ .../external/handlebars-1.0.rc.4.js | 2239 ---------- .../javascripts/external_development/ember.js | 3846 ++++++----------- .../javascripts/external_production/ember.js | 3483 ++++++--------- config/application.rb | 2 +- spec/javascripts/spec.js | 2 +- 9 files changed, 4691 insertions(+), 7120 deletions(-) create mode 100644 app/assets/javascripts/external/handlebars-1.0.rc.3.js delete mode 100755 app/assets/javascripts/external/handlebars-1.0.rc.4.js diff --git a/Gemfile b/Gemfile index 07ffce2b1..9035e2c03 100644 --- a/Gemfile +++ b/Gemfile @@ -4,10 +4,8 @@ gem 'active_model_serializers', git: 'https://github.com/rails-api/active_model_ # we had issues with latest, stick to the rev till we figure this out # PR that makes it all hang together welcome -gem 'ember-rails' -gem 'ember-source', '1.0.0.rc5' # or the version you need -gem 'handlebars-source', '1.0.0.rc4' # or the version you need -gem 'barber' +gem 'ember-rails', git: 'https://github.com/emberjs/ember-rails.git', ref: '57bbe32' +gem 'barber', '0.3.0' gem 'vestal_versions', git: 'https://github.com/zhangyuan/vestal_versions' diff --git a/Gemfile.lock b/Gemfile.lock index 56be9415e..7ce99ac7d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -53,6 +53,17 @@ GIT multi_json omniauth (~> 1.0) +GIT + remote: https://github.com/emberjs/ember-rails.git + revision: 57bbe3202725e55a8e4eaccba83d663b26bcf024 + ref: 57bbe32 + specs: + ember-rails (0.10.1) + active_model_serializers + barber + execjs (>= 1.2) + railties (>= 3.1) + GIT remote: https://github.com/rails-api/active_model_serializers.git revision: fe84e0ad5268f1439123fd9a1f9fd12e25a54cd3 @@ -131,10 +142,8 @@ GEM activesupport builder arel (3.0.2) - barber (0.4.2) - ember-source + barber (0.3.0) execjs - handlebars-source better_errors (0.8.0) coderay (>= 1.0.0) erubis (>= 2.6.6) @@ -165,13 +174,6 @@ GEM diffy (2.1.4) em-redis (0.3.0) eventmachine - ember-rails (0.10.0) - active_model_serializers - barber - execjs (>= 1.2) - railties (>= 3.1) - ember-source (1.0.0.rc5) - handlebars-source (= 1.0.0.rc4) erubis (2.7.0) eventmachine (1.0.3) excon (0.20.1) @@ -222,7 +224,6 @@ GEM spork (>= 0.8.4) haml (4.0.2) tilt - handlebars-source (1.0.0.rc4) has_ip_address (0.0.1) hashie (2.0.4) highline (1.6.18) @@ -482,7 +483,7 @@ DEPENDENCIES activerecord-postgres-hstore airbrake (= 3.1.2) annotate! - barber + barber (= 0.3.0) better_errors binding_of_caller certified @@ -491,8 +492,7 @@ DEPENDENCIES discourse_emoji! discourse_plugin! em-redis - ember-rails - ember-source (= 1.0.0.rc5) + ember-rails! eventmachine fabrication fakeweb (~> 1.3.0) @@ -505,7 +505,6 @@ DEPENDENCIES guard-jshint-on-rails guard-rspec guard-spork - handlebars-source (= 1.0.0.rc4) has_ip_address hiredis image_optim diff --git a/app/assets/javascripts/application.js.erb b/app/assets/javascripts/application.js.erb index ed4bc6e87..59821821a 100644 --- a/app/assets/javascripts/application.js.erb +++ b/app/assets/javascripts/application.js.erb @@ -12,7 +12,7 @@ // Externals we need to load first //= require ./external/jquery-1.8.3.js //= require ./external/jquery.ui.widget.js -//= require ./external/handlebars-1.0.rc.4.js +//= require ./external/handlebars-1.0.rc.3.js <% if Rails.env.development? require_asset ("./external_development/ember.js") diff --git a/app/assets/javascripts/external/handlebars-1.0.rc.3.js b/app/assets/javascripts/external/handlebars-1.0.rc.3.js new file mode 100644 index 000000000..4f1096df6 --- /dev/null +++ b/app/assets/javascripts/external/handlebars-1.0.rc.3.js @@ -0,0 +1,2202 @@ +/* + +Copyright (C) 2011 by Yehuda Katz + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +// lib/handlebars/base.js + +/*jshint eqnull:true*/ +this.Handlebars = {}; + +(function(Handlebars) { + +Handlebars.VERSION = "1.0.0-rc.3"; +Handlebars.COMPILER_REVISION = 2; + +Handlebars.REVISION_CHANGES = { + 1: '<= 1.0.rc.2', // 1.0.rc.2 is actually rev2 but doesn't report it + 2: '>= 1.0.0-rc.3' +}; + +Handlebars.helpers = {}; +Handlebars.partials = {}; + +Handlebars.registerHelper = function(name, fn, inverse) { + if(inverse) { fn.not = inverse; } + this.helpers[name] = fn; +}; + +Handlebars.registerPartial = function(name, str) { + this.partials[name] = str; +}; + +Handlebars.registerHelper('helperMissing', function(arg) { + if(arguments.length === 2) { + return undefined; + } else { + throw new Error("Could not find property '" + arg + "'"); + } +}); + +var toString = Object.prototype.toString, functionType = "[object Function]"; + +Handlebars.registerHelper('blockHelperMissing', function(context, options) { + var inverse = options.inverse || function() {}, fn = options.fn; + + + var ret = ""; + var type = toString.call(context); + + if(type === functionType) { context = context.call(this); } + + if(context === true) { + return fn(this); + } else if(context === false || context == null) { + return inverse(this); + } else if(type === "[object Array]") { + if(context.length > 0) { + return Handlebars.helpers.each(context, options); + } else { + return inverse(this); + } + } else { + return fn(context); + } +}); + +Handlebars.K = function() {}; + +Handlebars.createFrame = Object.create || function(object) { + Handlebars.K.prototype = object; + var obj = new Handlebars.K(); + Handlebars.K.prototype = null; + return obj; +}; + +Handlebars.logger = { + DEBUG: 0, INFO: 1, WARN: 2, ERROR: 3, level: 3, + + methodMap: {0: 'debug', 1: 'info', 2: 'warn', 3: 'error'}, + + // can be overridden in the host environment + log: function(level, obj) { + if (Handlebars.logger.level <= level) { + var method = Handlebars.logger.methodMap[level]; + if (typeof console !== 'undefined' && console[method]) { + console[method].call(console, obj); + } + } + } +}; + +Handlebars.log = function(level, obj) { Handlebars.logger.log(level, obj); }; + +Handlebars.registerHelper('each', function(context, options) { + var fn = options.fn, inverse = options.inverse; + var i = 0, ret = "", data; + + if (options.data) { + data = Handlebars.createFrame(options.data); + } + + if(context && typeof context === 'object') { + if(context instanceof Array){ + for(var j = context.length; i 2) { + expected.push("'" + this.terminals_[p] + "'"); + } + if (this.lexer.showPosition) { + errStr = "Parse error on line " + (yylineno + 1) + ":\n" + this.lexer.showPosition() + "\nExpecting " + expected.join(", ") + ", got '" + (this.terminals_[symbol] || symbol) + "'"; + } else { + errStr = "Parse error on line " + (yylineno + 1) + ": Unexpected " + (symbol == 1?"end of input":"'" + (this.terminals_[symbol] || symbol) + "'"); + } + this.parseError(errStr, {text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected}); + } + } + if (action[0] instanceof Array && action.length > 1) { + throw new Error("Parse Error: multiple actions possible at state: " + state + ", token: " + symbol); + } + switch (action[0]) { + case 1: + stack.push(symbol); + vstack.push(this.lexer.yytext); + lstack.push(this.lexer.yylloc); + stack.push(action[1]); + symbol = null; + if (!preErrorSymbol) { + yyleng = this.lexer.yyleng; + yytext = this.lexer.yytext; + yylineno = this.lexer.yylineno; + yyloc = this.lexer.yylloc; + if (recovering > 0) + recovering--; + } else { + symbol = preErrorSymbol; + preErrorSymbol = null; + } + break; + case 2: + len = this.productions_[action[1]][1]; + yyval.$ = vstack[vstack.length - len]; + yyval._$ = {first_line: lstack[lstack.length - (len || 1)].first_line, last_line: lstack[lstack.length - 1].last_line, first_column: lstack[lstack.length - (len || 1)].first_column, last_column: lstack[lstack.length - 1].last_column}; + if (ranges) { + yyval._$.range = [lstack[lstack.length - (len || 1)].range[0], lstack[lstack.length - 1].range[1]]; + } + r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack); + if (typeof r !== "undefined") { + return r; + } + if (len) { + stack = stack.slice(0, -1 * len * 2); + vstack = vstack.slice(0, -1 * len); + lstack = lstack.slice(0, -1 * len); + } + stack.push(this.productions_[action[1]][0]); + vstack.push(yyval.$); + lstack.push(yyval._$); + newState = table[stack[stack.length - 2]][stack[stack.length - 1]]; + stack.push(newState); + break; + case 3: + return true; + } + } + return true; +} +}; +/* Jison generated lexer */ +var lexer = (function(){ +var lexer = ({EOF:1, +parseError:function parseError(str, hash) { + if (this.yy.parser) { + this.yy.parser.parseError(str, hash); + } else { + throw new Error(str); + } + }, +setInput:function (input) { + this._input = input; + this._more = this._less = this.done = false; + this.yylineno = this.yyleng = 0; + this.yytext = this.matched = this.match = ''; + this.conditionStack = ['INITIAL']; + this.yylloc = {first_line:1,first_column:0,last_line:1,last_column:0}; + if (this.options.ranges) this.yylloc.range = [0,0]; + this.offset = 0; + return this; + }, +input:function () { + var ch = this._input[0]; + this.yytext += ch; + this.yyleng++; + this.offset++; + this.match += ch; + this.matched += ch; + var lines = ch.match(/(?:\r\n?|\n).*/g); + if (lines) { + this.yylineno++; + this.yylloc.last_line++; + } else { + this.yylloc.last_column++; + } + if (this.options.ranges) this.yylloc.range[1]++; + + this._input = this._input.slice(1); + return ch; + }, +unput:function (ch) { + var len = ch.length; + var lines = ch.split(/(?:\r\n?|\n)/g); + + this._input = ch + this._input; + this.yytext = this.yytext.substr(0, this.yytext.length-len-1); + //this.yyleng -= len; + this.offset -= len; + var oldLines = this.match.split(/(?:\r\n?|\n)/g); + this.match = this.match.substr(0, this.match.length-1); + this.matched = this.matched.substr(0, this.matched.length-1); + + if (lines.length-1) this.yylineno -= lines.length-1; + var r = this.yylloc.range; + + this.yylloc = {first_line: this.yylloc.first_line, + last_line: this.yylineno+1, + first_column: this.yylloc.first_column, + last_column: lines ? + (lines.length === oldLines.length ? this.yylloc.first_column : 0) + oldLines[oldLines.length - lines.length].length - lines[0].length: + this.yylloc.first_column - len + }; + + if (this.options.ranges) { + this.yylloc.range = [r[0], r[0] + this.yyleng - len]; + } + return this; + }, +more:function () { + this._more = true; + return this; + }, +less:function (n) { + this.unput(this.match.slice(n)); + }, +pastInput:function () { + var past = this.matched.substr(0, this.matched.length - this.match.length); + return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, ""); + }, +upcomingInput:function () { + var next = this.match; + if (next.length < 20) { + next += this._input.substr(0, 20-next.length); + } + return (next.substr(0,20)+(next.length > 20 ? '...':'')).replace(/\n/g, ""); + }, +showPosition:function () { + var pre = this.pastInput(); + var c = new Array(pre.length + 1).join("-"); + return pre + this.upcomingInput() + "\n" + c+"^"; + }, +next:function () { + if (this.done) { + return this.EOF; + } + if (!this._input) this.done = true; + + var token, + match, + tempMatch, + index, + col, + lines; + if (!this._more) { + this.yytext = ''; + this.match = ''; + } + var rules = this._currentRules(); + for (var i=0;i < rules.length; i++) { + tempMatch = this._input.match(this.rules[rules[i]]); + if (tempMatch && (!match || tempMatch[0].length > match[0].length)) { + match = tempMatch; + index = i; + if (!this.options.flex) break; + } + } + if (match) { + lines = match[0].match(/(?:\r\n?|\n).*/g); + if (lines) this.yylineno += lines.length; + this.yylloc = {first_line: this.yylloc.last_line, + last_line: this.yylineno+1, + first_column: this.yylloc.last_column, + last_column: lines ? lines[lines.length-1].length-lines[lines.length-1].match(/\r?\n?/)[0].length : this.yylloc.last_column + match[0].length}; + this.yytext += match[0]; + this.match += match[0]; + this.matches = match; + this.yyleng = this.yytext.length; + if (this.options.ranges) { + this.yylloc.range = [this.offset, this.offset += this.yyleng]; + } + this._more = false; + this._input = this._input.slice(match[0].length); + this.matched += match[0]; + token = this.performAction.call(this, this.yy, this, rules[index],this.conditionStack[this.conditionStack.length-1]); + if (this.done && this._input) this.done = false; + if (token) return token; + else return; + } + if (this._input === "") { + return this.EOF; + } else { + return this.parseError('Lexical error on line '+(this.yylineno+1)+'. Unrecognized text.\n'+this.showPosition(), + {text: "", token: null, line: this.yylineno}); + } + }, +lex:function lex() { + var r = this.next(); + if (typeof r !== 'undefined') { + return r; + } else { + return this.lex(); + } + }, +begin:function begin(condition) { + this.conditionStack.push(condition); + }, +popState:function popState() { + return this.conditionStack.pop(); + }, +_currentRules:function _currentRules() { + return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules; + }, +topState:function () { + return this.conditionStack[this.conditionStack.length-2]; + }, +pushState:function begin(condition) { + this.begin(condition); + }}); +lexer.options = {}; +lexer.performAction = function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) { + +var YYSTATE=YY_START +switch($avoiding_name_collisions) { +case 0: + if(yy_.yytext.slice(-1) !== "\\") this.begin("mu"); + if(yy_.yytext.slice(-1) === "\\") yy_.yytext = yy_.yytext.substr(0,yy_.yyleng-1), this.begin("emu"); + if(yy_.yytext) return 14; + +break; +case 1: return 14; +break; +case 2: + if(yy_.yytext.slice(-1) !== "\\") this.popState(); + if(yy_.yytext.slice(-1) === "\\") yy_.yytext = yy_.yytext.substr(0,yy_.yyleng-1); + return 14; + +break; +case 3: yy_.yytext = yy_.yytext.substr(0, yy_.yyleng-4); this.popState(); return 15; +break; +case 4: this.begin("par"); return 24; +break; +case 5: return 16; +break; +case 6: return 20; +break; +case 7: return 19; +break; +case 8: return 19; +break; +case 9: return 23; +break; +case 10: return 23; +break; +case 11: this.popState(); this.begin('com'); +break; +case 12: yy_.yytext = yy_.yytext.substr(3,yy_.yyleng-5); this.popState(); return 15; +break; +case 13: return 22; +break; +case 14: return 36; +break; +case 15: return 35; +break; +case 16: return 35; +break; +case 17: return 39; +break; +case 18: /*ignore whitespace*/ +break; +case 19: this.popState(); return 18; +break; +case 20: this.popState(); return 18; +break; +case 21: yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2).replace(/\\"/g,'"'); return 30; +break; +case 22: yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2).replace(/\\'/g,"'"); return 30; +break; +case 23: yy_.yytext = yy_.yytext.substr(1); return 28; +break; +case 24: return 32; +break; +case 25: return 32; +break; +case 26: return 31; +break; +case 27: return 35; +break; +case 28: yy_.yytext = yy_.yytext.substr(1, yy_.yyleng-2); return 35; +break; +case 29: return 'INVALID'; +break; +case 30: /*ignore whitespace*/ +break; +case 31: this.popState(); return 37; +break; +case 32: return 5; +break; +} +}; +lexer.rules = [/^(?:[^\x00]*?(?=(\{\{)))/,/^(?:[^\x00]+)/,/^(?:[^\x00]{2,}?(?=(\{\{|$)))/,/^(?:[\s\S]*?--\}\})/,/^(?:\{\{>)/,/^(?:\{\{#)/,/^(?:\{\{\/)/,/^(?:\{\{\^)/,/^(?:\{\{\s*else\b)/,/^(?:\{\{\{)/,/^(?:\{\{&)/,/^(?:\{\{!--)/,/^(?:\{\{![\s\S]*?\}\})/,/^(?:\{\{)/,/^(?:=)/,/^(?:\.(?=[} ]))/,/^(?:\.\.)/,/^(?:[\/.])/,/^(?:\s+)/,/^(?:\}\}\})/,/^(?:\}\})/,/^(?:"(\\["]|[^"])*")/,/^(?:'(\\[']|[^'])*')/,/^(?:@[a-zA-Z]+)/,/^(?:true(?=[}\s]))/,/^(?:false(?=[}\s]))/,/^(?:[0-9]+(?=[}\s]))/,/^(?:[a-zA-Z0-9_$-]+(?=[=}\s\/.]))/,/^(?:\[[^\]]*\])/,/^(?:.)/,/^(?:\s+)/,/^(?:[a-zA-Z0-9_$-/]+)/,/^(?:$)/]; +lexer.conditions = {"mu":{"rules":[4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,32],"inclusive":false},"emu":{"rules":[2],"inclusive":false},"com":{"rules":[3],"inclusive":false},"par":{"rules":[30,31],"inclusive":false},"INITIAL":{"rules":[0,1,32],"inclusive":true}}; +return lexer;})() +parser.lexer = lexer; +function Parser () { this.yy = {}; }Parser.prototype = parser;parser.Parser = Parser; +return new Parser; +})();; +// lib/handlebars/compiler/base.js +Handlebars.Parser = handlebars; + +Handlebars.parse = function(input) { + + // Just return if an already-compile AST was passed in. + if(input.constructor === Handlebars.AST.ProgramNode) { return input; } + + Handlebars.Parser.yy = Handlebars.AST; + return Handlebars.Parser.parse(input); +}; + +Handlebars.print = function(ast) { + return new Handlebars.PrintVisitor().accept(ast); +};; +// lib/handlebars/compiler/ast.js +(function() { + + Handlebars.AST = {}; + + Handlebars.AST.ProgramNode = function(statements, inverse) { + this.type = "program"; + this.statements = statements; + if(inverse) { this.inverse = new Handlebars.AST.ProgramNode(inverse); } + }; + + Handlebars.AST.MustacheNode = function(rawParams, hash, unescaped) { + this.type = "mustache"; + this.escaped = !unescaped; + this.hash = hash; + + var id = this.id = rawParams[0]; + var params = this.params = rawParams.slice(1); + + // a mustache is an eligible helper if: + // * its id is simple (a single part, not `this` or `..`) + var eligibleHelper = this.eligibleHelper = id.isSimple; + + // a mustache is definitely a helper if: + // * it is an eligible helper, and + // * it has at least one parameter or hash segment + this.isHelper = eligibleHelper && (params.length || hash); + + // if a mustache is an eligible helper but not a definite + // helper, it is ambiguous, and will be resolved in a later + // pass or at runtime. + }; + + Handlebars.AST.PartialNode = function(partialName, context) { + this.type = "partial"; + this.partialName = partialName; + this.context = context; + }; + + var verifyMatch = function(open, close) { + if(open.original !== close.original) { + throw new Handlebars.Exception(open.original + " doesn't match " + close.original); + } + }; + + Handlebars.AST.BlockNode = function(mustache, program, inverse, close) { + verifyMatch(mustache.id, close); + this.type = "block"; + this.mustache = mustache; + this.program = program; + this.inverse = inverse; + + if (this.inverse && !this.program) { + this.isInverse = true; + } + }; + + Handlebars.AST.ContentNode = function(string) { + this.type = "content"; + this.string = string; + }; + + Handlebars.AST.HashNode = function(pairs) { + this.type = "hash"; + this.pairs = pairs; + }; + + Handlebars.AST.IdNode = function(parts) { + this.type = "ID"; + this.original = parts.join("."); + + var dig = [], depth = 0; + + for(var i=0,l=parts.length; i 0) { throw new Handlebars.Exception("Invalid path: " + this.original); } + else if (part === "..") { depth++; } + else { this.isScoped = true; } + } + else { dig.push(part); } + } + + this.parts = dig; + this.string = dig.join('.'); + this.depth = depth; + + // an ID is simple if it only has one part, and that part is not + // `..` or `this`. + this.isSimple = parts.length === 1 && !this.isScoped && depth === 0; + + this.stringModeValue = this.string; + }; + + Handlebars.AST.PartialNameNode = function(name) { + this.type = "PARTIAL_NAME"; + this.name = name; + }; + + Handlebars.AST.DataNode = function(id) { + this.type = "DATA"; + this.id = id; + }; + + Handlebars.AST.StringNode = function(string) { + this.type = "STRING"; + this.string = string; + this.stringModeValue = string; + }; + + Handlebars.AST.IntegerNode = function(integer) { + this.type = "INTEGER"; + this.integer = integer; + this.stringModeValue = Number(integer); + }; + + Handlebars.AST.BooleanNode = function(bool) { + this.type = "BOOLEAN"; + this.bool = bool; + this.stringModeValue = bool === "true"; + }; + + Handlebars.AST.CommentNode = function(comment) { + this.type = "comment"; + this.comment = comment; + }; + +})();; +// lib/handlebars/utils.js + +var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack']; + +Handlebars.Exception = function(message) { + var tmp = Error.prototype.constructor.apply(this, arguments); + + // Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work. + for (var idx = 0; idx < errorProps.length; idx++) { + this[errorProps[idx]] = tmp[errorProps[idx]]; + } +}; +Handlebars.Exception.prototype = new Error(); + +// Build out our basic SafeString type +Handlebars.SafeString = function(string) { + this.string = string; +}; +Handlebars.SafeString.prototype.toString = function() { + return this.string.toString(); +}; + +(function() { + var escape = { + "&": "&", + "<": "<", + ">": ">", + '"': """, + "'": "'", + "`": "`" + }; + + var badChars = /[&<>"'`]/g; + var possible = /[&<>"'`]/; + + var escapeChar = function(chr) { + return escape[chr] || "&"; + }; + + Handlebars.Utils = { + escapeExpression: function(string) { + // don't escape SafeStrings, since they're already safe + if (string instanceof Handlebars.SafeString) { + return string.toString(); + } else if (string == null || string === false) { + return ""; + } + + if(!possible.test(string)) { return string; } + return string.replace(badChars, escapeChar); + }, + + isEmpty: function(value) { + if (!value && value !== 0) { + return true; + } else if(Object.prototype.toString.call(value) === "[object Array]" && value.length === 0) { + return true; + } else { + return false; + } + } + }; +})();; +// lib/handlebars/compiler/compiler.js + +/*jshint eqnull:true*/ +Handlebars.Compiler = function() {}; +Handlebars.JavaScriptCompiler = function() {}; + +(function(Compiler, JavaScriptCompiler) { + // the foundHelper register will disambiguate helper lookup from finding a + // function in a context. This is necessary for mustache compatibility, which + // requires that context functions in blocks are evaluated by blockHelperMissing, + // and then proceed as if the resulting value was provided to blockHelperMissing. + + Compiler.prototype = { + compiler: Compiler, + + disassemble: function() { + var opcodes = this.opcodes, opcode, out = [], params, param; + + for (var i=0, l=opcodes.length; i 0) { + this.source[1] = this.source[1] + ", " + locals.join(", "); + } + + // Generate minimizer alias mappings + if (!this.isChild) { + for (var alias in this.context.aliases) { + this.source[1] = this.source[1] + ', ' + alias + '=' + this.context.aliases[alias]; + } + } + + if (this.source[1]) { + this.source[1] = "var " + this.source[1].substring(2) + ";"; + } + + // Merge children + if (!this.isChild) { + this.source[1] += '\n' + this.context.programs.join('\n') + '\n'; + } + + if (!this.environment.isSimple) { + this.source.push("return buffer;"); + } + + var params = this.isChild ? ["depth0", "data"] : ["Handlebars", "depth0", "helpers", "partials", "data"]; + + for(var i=0, l=this.environment.depths.list.length; i this.stackVars.length) { this.stackVars.push("stack" + this.stackSlot); } + return this.topStackName(); + }, + topStackName: function() { + return "stack" + this.stackSlot; + }, + flushInline: function() { + var inlineStack = this.inlineStack; + if (inlineStack.length) { + this.inlineStack = []; + for (var i = 0, len = inlineStack.length; i < len; i++) { + var entry = inlineStack[i]; + if (entry instanceof Literal) { + this.compileStack.push(entry); + } else { + this.pushStack(entry); + } + } + } + }, + isInline: function() { + return this.inlineStack.length; + }, + + popStack: function(wrapped) { + var inline = this.isInline(), + item = (inline ? this.inlineStack : this.compileStack).pop(); + + if (!wrapped && (item instanceof Literal)) { + return item.value; + } else { + if (!inline) { + this.stackSlot--; + } + return item; + } + }, + + topStack: function(wrapped) { + var stack = (this.isInline() ? this.inlineStack : this.compileStack), + item = stack[stack.length - 1]; + + if (!wrapped && (item instanceof Literal)) { + return item.value; + } else { + return item; + } + }, + + quotedString: function(str) { + return '"' + str + .replace(/\\/g, '\\\\') + .replace(/"/g, '\\"') + .replace(/\n/g, '\\n') + .replace(/\r/g, '\\r') + '"'; + }, + + setupHelper: function(paramSize, name, missingParams) { + var params = []; + this.setupParams(paramSize, params, missingParams); + var foundHelper = this.nameLookup('helpers', name, 'helper'); + + return { + params: params, + name: foundHelper, + callParams: ["depth0"].concat(params).join(", "), + helperMissingParams: missingParams && ["depth0", this.quotedString(name)].concat(params).join(", ") + }; + }, + + // the params and contexts arguments are passed in arrays + // to fill in + setupParams: function(paramSize, params, useRegister) { + var options = [], contexts = [], types = [], param, inverse, program; + + options.push("hash:" + this.popStack()); + + inverse = this.popStack(); + program = this.popStack(); + + // Avoid setting fn and inverse if neither are set. This allows + // helpers to do a check for `if (options.fn)` + if (program || inverse) { + if (!program) { + this.context.aliases.self = "this"; + program = "self.noop"; + } + + if (!inverse) { + this.context.aliases.self = "this"; + inverse = "self.noop"; + } + + options.push("inverse:" + inverse); + options.push("fn:" + program); + } + + for(var i=0; i= 1.0.0-rc.4' -}; - -Handlebars.helpers = {}; -Handlebars.partials = {}; - -var toString = Object.prototype.toString, - functionType = '[object Function]', - objectType = '[object Object]'; - -Handlebars.registerHelper = function(name, fn, inverse) { - if (toString.call(name) === objectType) { - if (inverse || fn) { throw new Handlebars.Exception('Arg not supported with multiple helpers'); } - Handlebars.Utils.extend(this.helpers, name); - } else { - if (inverse) { fn.not = inverse; } - this.helpers[name] = fn; - } -}; - -Handlebars.registerPartial = function(name, str) { - if (toString.call(name) === objectType) { - Handlebars.Utils.extend(this.partials, name); - } else { - this.partials[name] = str; - } -}; - -Handlebars.registerHelper('helperMissing', function(arg) { - if(arguments.length === 2) { - return undefined; - } else { - throw new Error("Could not find property '" + arg + "'"); - } -}); - -Handlebars.registerHelper('blockHelperMissing', function(context, options) { - var inverse = options.inverse || function() {}, fn = options.fn; - - var type = toString.call(context); - - if(type === functionType) { context = context.call(this); } - - if(context === true) { - return fn(this); - } else if(context === false || context == null) { - return inverse(this); - } else if(type === "[object Array]") { - if(context.length > 0) { - return Handlebars.helpers.each(context, options); - } else { - return inverse(this); - } - } else { - return fn(context); - } -}); - -Handlebars.K = function() {}; - -Handlebars.createFrame = Object.create || function(object) { - Handlebars.K.prototype = object; - var obj = new Handlebars.K(); - Handlebars.K.prototype = null; - return obj; -}; - -Handlebars.logger = { - DEBUG: 0, INFO: 1, WARN: 2, ERROR: 3, level: 3, - - methodMap: {0: 'debug', 1: 'info', 2: 'warn', 3: 'error'}, - - // can be overridden in the host environment - log: function(level, obj) { - if (Handlebars.logger.level <= level) { - var method = Handlebars.logger.methodMap[level]; - if (typeof console !== 'undefined' && console[method]) { - console[method].call(console, obj); - } - } - } -}; - -Handlebars.log = function(level, obj) { Handlebars.logger.log(level, obj); }; - -Handlebars.registerHelper('each', function(context, options) { - var fn = options.fn, inverse = options.inverse; - var i = 0, ret = "", data; - - if (options.data) { - data = Handlebars.createFrame(options.data); - } - - if(context && typeof context === 'object') { - if(context instanceof Array){ - for(var j = context.length; i 2) { - expected.push("'" + this.terminals_[p] + "'"); - } - if (this.lexer.showPosition) { - errStr = "Parse error on line " + (yylineno + 1) + ":\n" + this.lexer.showPosition() + "\nExpecting " + expected.join(", ") + ", got '" + (this.terminals_[symbol] || symbol) + "'"; - } else { - errStr = "Parse error on line " + (yylineno + 1) + ": Unexpected " + (symbol == 1?"end of input":"'" + (this.terminals_[symbol] || symbol) + "'"); - } - this.parseError(errStr, {text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected}); - } - } - if (action[0] instanceof Array && action.length > 1) { - throw new Error("Parse Error: multiple actions possible at state: " + state + ", token: " + symbol); - } - switch (action[0]) { - case 1: - stack.push(symbol); - vstack.push(this.lexer.yytext); - lstack.push(this.lexer.yylloc); - stack.push(action[1]); - symbol = null; - if (!preErrorSymbol) { - yyleng = this.lexer.yyleng; - yytext = this.lexer.yytext; - yylineno = this.lexer.yylineno; - yyloc = this.lexer.yylloc; - if (recovering > 0) - recovering--; - } else { - symbol = preErrorSymbol; - preErrorSymbol = null; - } - break; - case 2: - len = this.productions_[action[1]][1]; - yyval.$ = vstack[vstack.length - len]; - yyval._$ = {first_line: lstack[lstack.length - (len || 1)].first_line, last_line: lstack[lstack.length - 1].last_line, first_column: lstack[lstack.length - (len || 1)].first_column, last_column: lstack[lstack.length - 1].last_column}; - if (ranges) { - yyval._$.range = [lstack[lstack.length - (len || 1)].range[0], lstack[lstack.length - 1].range[1]]; - } - r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack); - if (typeof r !== "undefined") { - return r; - } - if (len) { - stack = stack.slice(0, -1 * len * 2); - vstack = vstack.slice(0, -1 * len); - lstack = lstack.slice(0, -1 * len); - } - stack.push(this.productions_[action[1]][0]); - vstack.push(yyval.$); - lstack.push(yyval._$); - newState = table[stack[stack.length - 2]][stack[stack.length - 1]]; - stack.push(newState); - break; - case 3: - return true; - } - } - return true; -} -}; -/* Jison generated lexer */ -var lexer = (function(){ -var lexer = ({EOF:1, -parseError:function parseError(str, hash) { - if (this.yy.parser) { - this.yy.parser.parseError(str, hash); - } else { - throw new Error(str); - } - }, -setInput:function (input) { - this._input = input; - this._more = this._less = this.done = false; - this.yylineno = this.yyleng = 0; - this.yytext = this.matched = this.match = ''; - this.conditionStack = ['INITIAL']; - this.yylloc = {first_line:1,first_column:0,last_line:1,last_column:0}; - if (this.options.ranges) this.yylloc.range = [0,0]; - this.offset = 0; - return this; - }, -input:function () { - var ch = this._input[0]; - this.yytext += ch; - this.yyleng++; - this.offset++; - this.match += ch; - this.matched += ch; - var lines = ch.match(/(?:\r\n?|\n).*/g); - if (lines) { - this.yylineno++; - this.yylloc.last_line++; - } else { - this.yylloc.last_column++; - } - if (this.options.ranges) this.yylloc.range[1]++; - - this._input = this._input.slice(1); - return ch; - }, -unput:function (ch) { - var len = ch.length; - var lines = ch.split(/(?:\r\n?|\n)/g); - - this._input = ch + this._input; - this.yytext = this.yytext.substr(0, this.yytext.length-len-1); - //this.yyleng -= len; - this.offset -= len; - var oldLines = this.match.split(/(?:\r\n?|\n)/g); - this.match = this.match.substr(0, this.match.length-1); - this.matched = this.matched.substr(0, this.matched.length-1); - - if (lines.length-1) this.yylineno -= lines.length-1; - var r = this.yylloc.range; - - this.yylloc = {first_line: this.yylloc.first_line, - last_line: this.yylineno+1, - first_column: this.yylloc.first_column, - last_column: lines ? - (lines.length === oldLines.length ? this.yylloc.first_column : 0) + oldLines[oldLines.length - lines.length].length - lines[0].length: - this.yylloc.first_column - len - }; - - if (this.options.ranges) { - this.yylloc.range = [r[0], r[0] + this.yyleng - len]; - } - return this; - }, -more:function () { - this._more = true; - return this; - }, -less:function (n) { - this.unput(this.match.slice(n)); - }, -pastInput:function () { - var past = this.matched.substr(0, this.matched.length - this.match.length); - return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, ""); - }, -upcomingInput:function () { - var next = this.match; - if (next.length < 20) { - next += this._input.substr(0, 20-next.length); - } - return (next.substr(0,20)+(next.length > 20 ? '...':'')).replace(/\n/g, ""); - }, -showPosition:function () { - var pre = this.pastInput(); - var c = new Array(pre.length + 1).join("-"); - return pre + this.upcomingInput() + "\n" + c+"^"; - }, -next:function () { - if (this.done) { - return this.EOF; - } - if (!this._input) this.done = true; - - var token, - match, - tempMatch, - index, - col, - lines; - if (!this._more) { - this.yytext = ''; - this.match = ''; - } - var rules = this._currentRules(); - for (var i=0;i < rules.length; i++) { - tempMatch = this._input.match(this.rules[rules[i]]); - if (tempMatch && (!match || tempMatch[0].length > match[0].length)) { - match = tempMatch; - index = i; - if (!this.options.flex) break; - } - } - if (match) { - lines = match[0].match(/(?:\r\n?|\n).*/g); - if (lines) this.yylineno += lines.length; - this.yylloc = {first_line: this.yylloc.last_line, - last_line: this.yylineno+1, - first_column: this.yylloc.last_column, - last_column: lines ? lines[lines.length-1].length-lines[lines.length-1].match(/\r?\n?/)[0].length : this.yylloc.last_column + match[0].length}; - this.yytext += match[0]; - this.match += match[0]; - this.matches = match; - this.yyleng = this.yytext.length; - if (this.options.ranges) { - this.yylloc.range = [this.offset, this.offset += this.yyleng]; - } - this._more = false; - this._input = this._input.slice(match[0].length); - this.matched += match[0]; - token = this.performAction.call(this, this.yy, this, rules[index],this.conditionStack[this.conditionStack.length-1]); - if (this.done && this._input) this.done = false; - if (token) return token; - else return; - } - if (this._input === "") { - return this.EOF; - } else { - return this.parseError('Lexical error on line '+(this.yylineno+1)+'. Unrecognized text.\n'+this.showPosition(), - {text: "", token: null, line: this.yylineno}); - } - }, -lex:function lex() { - var r = this.next(); - if (typeof r !== 'undefined') { - return r; - } else { - return this.lex(); - } - }, -begin:function begin(condition) { - this.conditionStack.push(condition); - }, -popState:function popState() { - return this.conditionStack.pop(); - }, -_currentRules:function _currentRules() { - return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules; - }, -topState:function () { - return this.conditionStack[this.conditionStack.length-2]; - }, -pushState:function begin(condition) { - this.begin(condition); - }}); -lexer.options = {}; -lexer.performAction = function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) { - -var YYSTATE=YY_START -switch($avoiding_name_collisions) { -case 0: yy_.yytext = "\\"; return 14; -break; -case 1: - if(yy_.yytext.slice(-1) !== "\\") this.begin("mu"); - if(yy_.yytext.slice(-1) === "\\") yy_.yytext = yy_.yytext.substr(0,yy_.yyleng-1), this.begin("emu"); - if(yy_.yytext) return 14; - -break; -case 2: return 14; -break; -case 3: - if(yy_.yytext.slice(-1) !== "\\") this.popState(); - if(yy_.yytext.slice(-1) === "\\") yy_.yytext = yy_.yytext.substr(0,yy_.yyleng-1); - return 14; - -break; -case 4: yy_.yytext = yy_.yytext.substr(0, yy_.yyleng-4); this.popState(); return 15; -break; -case 5: this.begin("par"); return 24; -break; -case 6: return 16; -break; -case 7: return 20; -break; -case 8: return 19; -break; -case 9: return 19; -break; -case 10: return 23; -break; -case 11: return 23; -break; -case 12: this.popState(); this.begin('com'); -break; -case 13: yy_.yytext = yy_.yytext.substr(3,yy_.yyleng-5); this.popState(); return 15; -break; -case 14: return 22; -break; -case 15: return 36; -break; -case 16: return 35; -break; -case 17: return 35; -break; -case 18: return 39; -break; -case 19: /*ignore whitespace*/ -break; -case 20: this.popState(); return 18; -break; -case 21: this.popState(); return 18; -break; -case 22: yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2).replace(/\\"/g,'"'); return 30; -break; -case 23: yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2).replace(/\\'/g,"'"); return 30; -break; -case 24: yy_.yytext = yy_.yytext.substr(1); return 28; -break; -case 25: return 32; -break; -case 26: return 32; -break; -case 27: return 31; -break; -case 28: return 35; -break; -case 29: yy_.yytext = yy_.yytext.substr(1, yy_.yyleng-2); return 35; -break; -case 30: return 'INVALID'; -break; -case 31: /*ignore whitespace*/ -break; -case 32: this.popState(); return 37; -break; -case 33: return 5; -break; -} -}; -lexer.rules = [/^(?:\\\\(?=(\{\{)))/,/^(?:[^\x00]*?(?=(\{\{)))/,/^(?:[^\x00]+)/,/^(?:[^\x00]{2,}?(?=(\{\{|$)))/,/^(?:[\s\S]*?--\}\})/,/^(?:\{\{>)/,/^(?:\{\{#)/,/^(?:\{\{\/)/,/^(?:\{\{\^)/,/^(?:\{\{\s*else\b)/,/^(?:\{\{\{)/,/^(?:\{\{&)/,/^(?:\{\{!--)/,/^(?:\{\{![\s\S]*?\}\})/,/^(?:\{\{)/,/^(?:=)/,/^(?:\.(?=[}/ ]))/,/^(?:\.\.)/,/^(?:[\/.])/,/^(?:\s+)/,/^(?:\}\}\})/,/^(?:\}\})/,/^(?:"(\\["]|[^"])*")/,/^(?:'(\\[']|[^'])*')/,/^(?:@[a-zA-Z]+)/,/^(?:true(?=[}\s]))/,/^(?:false(?=[}\s]))/,/^(?:-?[0-9]+(?=[}\s]))/,/^(?:[a-zA-Z0-9_$:\-]+(?=[=}\s\/.]))/,/^(?:\[[^\]]*\])/,/^(?:.)/,/^(?:\s+)/,/^(?:[a-zA-Z0-9_$\-\/]+)/,/^(?:$)/]; -lexer.conditions = {"mu":{"rules":[5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,33],"inclusive":false},"emu":{"rules":[3],"inclusive":false},"com":{"rules":[4],"inclusive":false},"par":{"rules":[31,32],"inclusive":false},"INITIAL":{"rules":[0,1,2,33],"inclusive":true}}; -return lexer;})() -parser.lexer = lexer; -function Parser () { this.yy = {}; }Parser.prototype = parser;parser.Parser = Parser; -return new Parser; -})();; -// lib/handlebars/compiler/base.js - -Handlebars.Parser = handlebars; - -Handlebars.parse = function(input) { - - // Just return if an already-compile AST was passed in. - if(input.constructor === Handlebars.AST.ProgramNode) { return input; } - - Handlebars.Parser.yy = Handlebars.AST; - return Handlebars.Parser.parse(input); -}; -; -// lib/handlebars/compiler/ast.js -Handlebars.AST = {}; - -Handlebars.AST.ProgramNode = function(statements, inverse) { - this.type = "program"; - this.statements = statements; - if(inverse) { this.inverse = new Handlebars.AST.ProgramNode(inverse); } -}; - -Handlebars.AST.MustacheNode = function(rawParams, hash, unescaped) { - this.type = "mustache"; - this.escaped = !unescaped; - this.hash = hash; - - var id = this.id = rawParams[0]; - var params = this.params = rawParams.slice(1); - - // a mustache is an eligible helper if: - // * its id is simple (a single part, not `this` or `..`) - var eligibleHelper = this.eligibleHelper = id.isSimple; - - // a mustache is definitely a helper if: - // * it is an eligible helper, and - // * it has at least one parameter or hash segment - this.isHelper = eligibleHelper && (params.length || hash); - - // if a mustache is an eligible helper but not a definite - // helper, it is ambiguous, and will be resolved in a later - // pass or at runtime. -}; - -Handlebars.AST.PartialNode = function(partialName, context) { - this.type = "partial"; - this.partialName = partialName; - this.context = context; -}; - -Handlebars.AST.BlockNode = function(mustache, program, inverse, close) { - var verifyMatch = function(open, close) { - if(open.original !== close.original) { - throw new Handlebars.Exception(open.original + " doesn't match " + close.original); - } - }; - - verifyMatch(mustache.id, close); - this.type = "block"; - this.mustache = mustache; - this.program = program; - this.inverse = inverse; - - if (this.inverse && !this.program) { - this.isInverse = true; - } -}; - -Handlebars.AST.ContentNode = function(string) { - this.type = "content"; - this.string = string; -}; - -Handlebars.AST.HashNode = function(pairs) { - this.type = "hash"; - this.pairs = pairs; -}; - -Handlebars.AST.IdNode = function(parts) { - this.type = "ID"; - this.original = parts.join("."); - - var dig = [], depth = 0; - - for(var i=0,l=parts.length; i 0) { throw new Handlebars.Exception("Invalid path: " + this.original); } - else if (part === "..") { depth++; } - else { this.isScoped = true; } - } - else { dig.push(part); } - } - - this.parts = dig; - this.string = dig.join('.'); - this.depth = depth; - - // an ID is simple if it only has one part, and that part is not - // `..` or `this`. - this.isSimple = parts.length === 1 && !this.isScoped && depth === 0; - - this.stringModeValue = this.string; -}; - -Handlebars.AST.PartialNameNode = function(name) { - this.type = "PARTIAL_NAME"; - this.name = name; -}; - -Handlebars.AST.DataNode = function(id) { - this.type = "DATA"; - this.id = id; -}; - -Handlebars.AST.StringNode = function(string) { - this.type = "STRING"; - this.string = string; - this.stringModeValue = string; -}; - -Handlebars.AST.IntegerNode = function(integer) { - this.type = "INTEGER"; - this.integer = integer; - this.stringModeValue = Number(integer); -}; - -Handlebars.AST.BooleanNode = function(bool) { - this.type = "BOOLEAN"; - this.bool = bool; - this.stringModeValue = bool === "true"; -}; - -Handlebars.AST.CommentNode = function(comment) { - this.type = "comment"; - this.comment = comment; -}; -; -// lib/handlebars/utils.js - -var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack']; - -Handlebars.Exception = function(message) { - var tmp = Error.prototype.constructor.apply(this, arguments); - - // Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work. - for (var idx = 0; idx < errorProps.length; idx++) { - this[errorProps[idx]] = tmp[errorProps[idx]]; - } -}; -Handlebars.Exception.prototype = new Error(); - -// Build out our basic SafeString type -Handlebars.SafeString = function(string) { - this.string = string; -}; -Handlebars.SafeString.prototype.toString = function() { - return this.string.toString(); -}; - -var escape = { - "&": "&", - "<": "<", - ">": ">", - '"': """, - "'": "'", - "`": "`" -}; - -var badChars = /[&<>"'`]/g; -var possible = /[&<>"'`]/; - -var escapeChar = function(chr) { - return escape[chr] || "&"; -}; - -Handlebars.Utils = { - extend: function(obj, value) { - for(var key in value) { - if(value.hasOwnProperty(key)) { - obj[key] = value[key]; - } - } - }, - - escapeExpression: function(string) { - // don't escape SafeStrings, since they're already safe - if (string instanceof Handlebars.SafeString) { - return string.toString(); - } else if (string == null || string === false) { - return ""; - } - - // Force a string conversion as this will be done by the append regardless and - // the regex test will do this transparently behind the scenes, causing issues if - // an object's to string has escaped characters in it. - string = string.toString(); - - if(!possible.test(string)) { return string; } - return string.replace(badChars, escapeChar); - }, - - isEmpty: function(value) { - if (!value && value !== 0) { - return true; - } else if(toString.call(value) === "[object Array]" && value.length === 0) { - return true; - } else { - return false; - } - } -}; -; -// lib/handlebars/compiler/compiler.js - -/*jshint eqnull:true*/ -var Compiler = Handlebars.Compiler = function() {}; -var JavaScriptCompiler = Handlebars.JavaScriptCompiler = function() {}; - -// the foundHelper register will disambiguate helper lookup from finding a -// function in a context. This is necessary for mustache compatibility, which -// requires that context functions in blocks are evaluated by blockHelperMissing, -// and then proceed as if the resulting value was provided to blockHelperMissing. - -Compiler.prototype = { - compiler: Compiler, - - disassemble: function() { - var opcodes = this.opcodes, opcode, out = [], params, param; - - for (var i=0, l=opcodes.length; i 0) { - this.source[1] = this.source[1] + ", " + locals.join(", "); - } - - // Generate minimizer alias mappings - if (!this.isChild) { - for (var alias in this.context.aliases) { - this.source[1] = this.source[1] + ', ' + alias + '=' + this.context.aliases[alias]; - } - } - - if (this.source[1]) { - this.source[1] = "var " + this.source[1].substring(2) + ";"; - } - - // Merge children - if (!this.isChild) { - this.source[1] += '\n' + this.context.programs.join('\n') + '\n'; - } - - if (!this.environment.isSimple) { - this.source.push("return buffer;"); - } - - var params = this.isChild ? ["depth0", "data"] : ["Handlebars", "depth0", "helpers", "partials", "data"]; - - for(var i=0, l=this.environment.depths.list.length; i this.stackVars.length) { this.stackVars.push("stack" + this.stackSlot); } - return this.topStackName(); - }, - topStackName: function() { - return "stack" + this.stackSlot; - }, - flushInline: function() { - var inlineStack = this.inlineStack; - if (inlineStack.length) { - this.inlineStack = []; - for (var i = 0, len = inlineStack.length; i < len; i++) { - var entry = inlineStack[i]; - if (entry instanceof Literal) { - this.compileStack.push(entry); - } else { - this.pushStack(entry); - } - } - } - }, - isInline: function() { - return this.inlineStack.length; - }, - - popStack: function(wrapped) { - var inline = this.isInline(), - item = (inline ? this.inlineStack : this.compileStack).pop(); - - if (!wrapped && (item instanceof Literal)) { - return item.value; - } else { - if (!inline) { - this.stackSlot--; - } - return item; - } - }, - - topStack: function(wrapped) { - var stack = (this.isInline() ? this.inlineStack : this.compileStack), - item = stack[stack.length - 1]; - - if (!wrapped && (item instanceof Literal)) { - return item.value; - } else { - return item; - } - }, - - quotedString: function(str) { - return '"' + str - .replace(/\\/g, '\\\\') - .replace(/"/g, '\\"') - .replace(/\n/g, '\\n') - .replace(/\r/g, '\\r') - .replace(/\u2028/g, '\\u2028') // Per Ecma-262 7.3 + 7.8.4 - .replace(/\u2029/g, '\\u2029') + '"'; - }, - - setupHelper: function(paramSize, name, missingParams) { - var params = []; - this.setupParams(paramSize, params, missingParams); - var foundHelper = this.nameLookup('helpers', name, 'helper'); - - return { - params: params, - name: foundHelper, - callParams: ["depth0"].concat(params).join(", "), - helperMissingParams: missingParams && ["depth0", this.quotedString(name)].concat(params).join(", ") - }; - }, - - // the params and contexts arguments are passed in arrays - // to fill in - setupParams: function(paramSize, params, useRegister) { - var options = [], contexts = [], types = [], param, inverse, program; - - options.push("hash:" + this.popStack()); - - inverse = this.popStack(); - program = this.popStack(); - - // Avoid setting fn and inverse if neither are set. This allows - // helpers to do a check for `if (options.fn)` - if (program || inverse) { - if (!program) { - this.context.aliases.self = "this"; - program = "self.noop"; - } - - if (!inverse) { - this.context.aliases.self = "this"; - inverse = "self.noop"; - } - - options.push("inverse:" + inverse); - options.push("fn:" + program); - } - - for(var i=0; i -1) { + list.splice(index, 1); + } + }, + + /** + @method isEmpty + @return {Boolean} + */ + isEmpty: function() { + return this.list.length === 0; + }, + + /** + @method has + @param obj + @return {Boolean} + */ + has: function(obj) { + var guid = guidFor(obj), + presenceSet = this.presenceSet; + + return guid in presenceSet; + }, + + /** + @method forEach + @param {Function} fn + @param self + */ + forEach: function(fn, self) { + // allow mutation during iteration + var list = this.toArray(); + + for (var i = 0, j = list.length; i < j; i++) { + fn.call(self, list[i]); + } + }, + + /** + @method toArray + @return {Array} + */ + toArray: function() { + return this.list.slice(); + }, + + /** + @method copy + @return {Ember.OrderedSet} + */ + copy: function() { + var set = new OrderedSet(); + + set.presenceSet = copy(this.presenceSet); + set.list = this.toArray(); + + return set; + } +}; + +/** + A Map stores values indexed by keys. Unlike JavaScript's + default Objects, the keys of a Map can be any JavaScript + object. + + Internally, a Map has two data structures: + + 1. `keys`: an OrderedSet of all of the existing keys + 2. `values`: a JavaScript Object indexed by the `Ember.guidFor(key)` + + When a key/value pair is added for the first time, we + add the key to the `keys` OrderedSet, and create or + replace an entry in `values`. When an entry is deleted, + we delete its entry in `keys` and `values`. + + @class Map + @namespace Ember + @private + @constructor +*/ +var Map = Ember.Map = function() { + this.keys = Ember.OrderedSet.create(); + this.values = {}; +}; + +/** + @method create + @static +*/ +Map.create = function() { + return new Map(); +}; + +Map.prototype = { + /** + Retrieve the value associated with a given key. + + @method get + @param {*} key + @return {*} the value associated with the key, or `undefined` + */ + get: function(key) { + var values = this.values, + guid = guidFor(key); + + return values[guid]; + }, + + /** + Adds a value to the map. If a value for the given key has already been + provided, the new value will replace the old value. + + @method set + @param {*} key + @param {*} value + */ + set: function(key, value) { + var keys = this.keys, + values = this.values, + guid = guidFor(key); + + keys.add(key); + values[guid] = value; + }, + + /** + Removes a value from the map for an associated key. + + @method remove + @param {*} key + @return {Boolean} true if an item was removed, false otherwise + */ + remove: function(key) { + // don't use ES6 "delete" because it will be annoying + // to use in browsers that are not ES6 friendly; + var keys = this.keys, + values = this.values, + guid = guidFor(key); + + if (values.hasOwnProperty(guid)) { + keys.remove(key); + delete values[guid]; + return true; + } else { + return false; + } + }, + + /** + Check whether a key is present. + + @method has + @param {*} key + @return {Boolean} true if the item was present, false otherwise + */ + has: function(key) { + var values = this.values, + guid = guidFor(key); + + return values.hasOwnProperty(guid); + }, + + /** + Iterate over all the keys and values. Calls the function once + for each key, passing in the key and value, in that order. + + The keys are guaranteed to be iterated over in insertion order. + + @method forEach + @param {Function} callback + @param {*} self if passed, the `this` value inside the + callback. By default, `this` is the map. + */ + forEach: function(callback, self) { + var keys = this.keys, + values = this.values; + + keys.forEach(function(key) { + var guid = guidFor(key); + callback.call(self, key, values[guid]); + }); + }, + + /** + @method copy + @return {Ember.Map} + */ + copy: function() { + return copyMap(this, new Map()); + } +}; + +/** + @class MapWithDefault + @namespace Ember + @extends Ember.Map + @private + @constructor + @param [options] + @param {*} [options.defaultValue] +*/ +var MapWithDefault = Ember.MapWithDefault = function(options) { + Map.call(this); + this.defaultValue = options.defaultValue; +}; + +/** + @method create + @static + @param [options] + @param {*} [options.defaultValue] + @return {Ember.MapWithDefault|Ember.Map} If options are passed, returns + `Ember.MapWithDefault` otherwise returns `Ember.Map` +*/ +MapWithDefault.create = function(options) { + if (options) { + return new MapWithDefault(options); + } else { + return new Map(); + } +}; + +MapWithDefault.prototype = Ember.create(Map.prototype); + +/** + Retrieve the value associated with a given key. + + @method get + @param {*} key + @return {*} the value associated with the key, or the default value +*/ +MapWithDefault.prototype.get = function(key) { + var hasValue = this.has(key); + + if (hasValue) { + return Map.prototype.get.call(this, key); + } else { + var defaultValue = this.defaultValue(key); + this.set(key, defaultValue); + return defaultValue; + } +}; + +/** + @method copy + @return {Ember.MapWithDefault} +*/ +MapWithDefault.prototype.copy = function() { + return copyMap(this, new MapWithDefault({ + defaultValue: this.defaultValue + })); +}; + +})(); + + + (function() { /** @module ember-metal @@ -1644,12 +1992,13 @@ get = function get(obj, keyName) { obj = null; } - Ember.assert("Cannot call get with '"+ keyName +"' on an undefined object.", obj !== undefined); - - if (obj === null || keyName.indexOf('.') !== -1) { + if (!obj || keyName.indexOf('.') !== -1) { + Ember.assert("Cannot call get with '"+ keyName +"' on an undefined object.", obj !== undefined); return getPath(obj, keyName); } + Ember.assert("You need to provide an object and key to `get`.", !!obj && keyName); + var meta = obj[META_KEY], desc = meta && meta.descs[keyName], ret; if (desc) { return desc.get(obj, keyName); @@ -1721,7 +2070,7 @@ var getPath = Ember._getPath = function(root, path) { parts = path.split("."); len = parts.length; - for (idx = 0; root != null && idx < len; idx++) { + for (idx=0; root && idx= 0; i--) { var target = actions[i][0], method = actions[i][1], - flags = actions[i][2], + once = actions[i][2], + suspended = actions[i][3], actionIndex = indexOf(otherActions, target, method); if (actionIndex === -1) { - otherActions.push([target, method, flags]); + otherActions.push([target, method, once, suspended]); } } } @@ -1847,13 +2194,14 @@ function actionsDiff(obj, eventName, otherActions) { for (var i = actions.length - 1; i >= 0; i--) { var target = actions[i][0], method = actions[i][1], - flags = actions[i][2], + once = actions[i][2], + suspended = actions[i][3], actionIndex = indexOf(otherActions, target, method); if (actionIndex !== -1) { continue; } - otherActions.push([target, method, flags]); - diffActions.push([target, method, flags]); + otherActions.push([target, method, once, suspended]); + diffActions.push([target, method, once, suspended]); } return diffActions; @@ -1879,14 +2227,11 @@ function addListener(obj, eventName, target, method, once) { } var actions = actionsFor(obj, eventName), - actionIndex = indexOf(actions, target, method), - flags = 0; - - if (once) flags |= ONCE; + actionIndex = indexOf(actions, target, method); if (actionIndex !== -1) { return; } - actions.push([target, method, flags]); + actions.push([target, method, once, undefined]); if ('function' === typeof obj.didAddListener) { obj.didAddListener(eventName, target, method); @@ -1896,7 +2241,7 @@ function addListener(obj, eventName, target, method, once) { /** Remove an event listener - Arguments should match those passed to `Ember.addListener`. + Arguments should match those passed to {{#crossLink "Ember/addListener"}}{{/crossLink}} @method removeListener @for Ember @@ -1913,7 +2258,7 @@ function removeListener(obj, eventName, target, method) { target = null; } - function _removeListener(target, method) { + function _removeListener(target, method, once) { var actions = actionsFor(obj, eventName), actionIndex = indexOf(actions, target, method); @@ -1970,12 +2315,12 @@ function suspendListener(obj, eventName, target, method, callback) { if (actionIndex !== -1) { action = actions[actionIndex].slice(); // copy it, otherwise we're modifying a shared object - action[2] |= SUSPENDED; // mark the action as suspended + action[3] = true; // mark the action as suspended actions[actionIndex] = action; // replace the shared object with our copy } function tryable() { return callback.call(target); } - function finalizer() { if (action) { action[2] &= ~SUSPENDED; } } + function finalizer() { if (action) { action[3] = undefined; } } return Ember.tryFinally(tryable, finalizer); } @@ -2014,7 +2359,7 @@ function suspendListeners(obj, eventNames, target, method, callback) { if (actionIndex !== -1) { action = actions[actionIndex].slice(); - action[2] |= SUSPENDED; + action[3] = true; actions[actionIndex] = action; suspendedActions.push(action); } @@ -2024,7 +2369,7 @@ function suspendListeners(obj, eventNames, target, method, callback) { function finalizer() { for (i = 0, l = suspendedActions.length; i < l; i++) { - suspendedActions[i][2] &= ~SUSPENDED; + suspendedActions[i][3] = undefined; } } @@ -2074,11 +2419,13 @@ function sendEvent(obj, eventName, params, actions) { if (!actions) { return; } for (var i = actions.length - 1; i >= 0; i--) { // looping in reverse for once listeners - var action = actions[i]; - if (!action) { continue; } - var target = action[0], method = action[1], flags = action[2]; - if (flags & SUSPENDED) { continue; } - if (flags & ONCE) { removeListener(obj, eventName, target, method); } + if (!actions[i] || actions[i][3] === true) { continue; } + + var target = actions[i][0], + method = actions[i][1], + once = actions[i][2]; + + if (once) { removeListener(obj, eventName, target, method); } if (!target) { target = obj; } if ('string' === typeof method) { method = target[method]; } if (params) { @@ -2158,7 +2505,7 @@ var guidFor = Ember.guidFor, keyName: keyName, eventName: eventName, listeners: [ - [target, method, flags] + [target, method, onceFlag, suspendedFlag] ] }, ... @@ -2387,11 +2734,11 @@ var endPropertyChanges = Ember.endPropertyChanges = function() { }); ``` - @method changeProperties + @method changePropertiess @param {Function} callback @param [binding] */ -Ember.changeProperties = function(cb, binding){ +var changeProperties = Ember.changeProperties = function(cb, binding){ beginPropertyChanges(); tryFinally(cb, endPropertyChanges, binding); }; @@ -2570,386 +2917,6 @@ Ember.trySetPath = Ember.deprecateFunc('trySetPath has been renamed to trySet', -(function() { -/** -@module ember-metal -*/ - -/* - JavaScript (before ES6) does not have a Map implementation. Objects, - which are often used as dictionaries, may only have Strings as keys. - - Because Ember has a way to get a unique identifier for every object - via `Ember.guidFor`, we can implement a performant Map with arbitrary - keys. Because it is commonly used in low-level bookkeeping, Map is - implemented as a pure JavaScript object for performance. - - This implementation follows the current iteration of the ES6 proposal for - maps (http://wiki.ecmascript.org/doku.php?id=harmony:simple_maps_and_sets), - with two exceptions. First, because we need our implementation to be pleasant - on older browsers, we do not use the `delete` name (using `remove` instead). - Second, as we do not have the luxury of in-VM iteration, we implement a - forEach method for iteration. - - Map is mocked out to look like an Ember object, so you can do - `Ember.Map.create()` for symmetry with other Ember classes. -*/ -var get = Ember.get, - set = Ember.set, - guidFor = Ember.guidFor, - indexOf = Ember.ArrayPolyfills.indexOf; - -var copy = function(obj) { - var output = {}; - - for (var prop in obj) { - if (obj.hasOwnProperty(prop)) { output[prop] = obj[prop]; } - } - - return output; -}; - -var copyMap = function(original, newObject) { - var keys = original.keys.copy(), - values = copy(original.values); - - newObject.keys = keys; - newObject.values = values; - newObject.length = original.length; - - return newObject; -}; - -/** - This class is used internally by Ember and Ember Data. - Please do not use it at this time. We plan to clean it up - and add many tests soon. - - @class OrderedSet - @namespace Ember - @constructor - @private -*/ -var OrderedSet = Ember.OrderedSet = function() { - this.clear(); -}; - -/** - @method create - @static - @return {Ember.OrderedSet} -*/ -OrderedSet.create = function() { - return new OrderedSet(); -}; - - -OrderedSet.prototype = { - /** - @method clear - */ - clear: function() { - this.presenceSet = {}; - this.list = []; - }, - - /** - @method add - @param obj - */ - add: function(obj) { - var guid = guidFor(obj), - presenceSet = this.presenceSet, - list = this.list; - - if (guid in presenceSet) { return; } - - presenceSet[guid] = true; - list.push(obj); - }, - - /** - @method remove - @param obj - */ - remove: function(obj) { - var guid = guidFor(obj), - presenceSet = this.presenceSet, - list = this.list; - - delete presenceSet[guid]; - - var index = indexOf.call(list, obj); - if (index > -1) { - list.splice(index, 1); - } - }, - - /** - @method isEmpty - @return {Boolean} - */ - isEmpty: function() { - return this.list.length === 0; - }, - - /** - @method has - @param obj - @return {Boolean} - */ - has: function(obj) { - var guid = guidFor(obj), - presenceSet = this.presenceSet; - - return guid in presenceSet; - }, - - /** - @method forEach - @param {Function} fn - @param self - */ - forEach: function(fn, self) { - // allow mutation during iteration - var list = this.toArray(); - - for (var i = 0, j = list.length; i < j; i++) { - fn.call(self, list[i]); - } - }, - - /** - @method toArray - @return {Array} - */ - toArray: function() { - return this.list.slice(); - }, - - /** - @method copy - @return {Ember.OrderedSet} - */ - copy: function() { - var set = new OrderedSet(); - - set.presenceSet = copy(this.presenceSet); - set.list = this.toArray(); - - return set; - } -}; - -/** - A Map stores values indexed by keys. Unlike JavaScript's - default Objects, the keys of a Map can be any JavaScript - object. - - Internally, a Map has two data structures: - - 1. `keys`: an OrderedSet of all of the existing keys - 2. `values`: a JavaScript Object indexed by the `Ember.guidFor(key)` - - When a key/value pair is added for the first time, we - add the key to the `keys` OrderedSet, and create or - replace an entry in `values`. When an entry is deleted, - we delete its entry in `keys` and `values`. - - @class Map - @namespace Ember - @private - @constructor -*/ -var Map = Ember.Map = function() { - this.keys = Ember.OrderedSet.create(); - this.values = {}; -}; - -/** - @method create - @static -*/ -Map.create = function() { - return new Map(); -}; - -Map.prototype = { - /** - This property will change as the number of objects in the map changes. - - @property length - @type number - @default 0 - */ - length: 0, - - - /** - Retrieve the value associated with a given key. - - @method get - @param {*} key - @return {*} the value associated with the key, or `undefined` - */ - get: function(key) { - var values = this.values, - guid = guidFor(key); - - return values[guid]; - }, - - /** - Adds a value to the map. If a value for the given key has already been - provided, the new value will replace the old value. - - @method set - @param {*} key - @param {*} value - */ - set: function(key, value) { - var keys = this.keys, - values = this.values, - guid = guidFor(key); - - keys.add(key); - values[guid] = value; - set(this, 'length', keys.list.length); - }, - - /** - Removes a value from the map for an associated key. - - @method remove - @param {*} key - @return {Boolean} true if an item was removed, false otherwise - */ - remove: function(key) { - // don't use ES6 "delete" because it will be annoying - // to use in browsers that are not ES6 friendly; - var keys = this.keys, - values = this.values, - guid = guidFor(key); - - if (values.hasOwnProperty(guid)) { - keys.remove(key); - delete values[guid]; - set(this, 'length', keys.list.length); - return true; - } else { - return false; - } - }, - - /** - Check whether a key is present. - - @method has - @param {*} key - @return {Boolean} true if the item was present, false otherwise - */ - has: function(key) { - var values = this.values, - guid = guidFor(key); - - return values.hasOwnProperty(guid); - }, - - /** - Iterate over all the keys and values. Calls the function once - for each key, passing in the key and value, in that order. - - The keys are guaranteed to be iterated over in insertion order. - - @method forEach - @param {Function} callback - @param {*} self if passed, the `this` value inside the - callback. By default, `this` is the map. - */ - forEach: function(callback, self) { - var keys = this.keys, - values = this.values; - - keys.forEach(function(key) { - var guid = guidFor(key); - callback.call(self, key, values[guid]); - }); - }, - - /** - @method copy - @return {Ember.Map} - */ - copy: function() { - return copyMap(this, new Map()); - } -}; - -/** - @class MapWithDefault - @namespace Ember - @extends Ember.Map - @private - @constructor - @param [options] - @param {*} [options.defaultValue] -*/ -var MapWithDefault = Ember.MapWithDefault = function(options) { - Map.call(this); - this.defaultValue = options.defaultValue; -}; - -/** - @method create - @static - @param [options] - @param {*} [options.defaultValue] - @return {Ember.MapWithDefault|Ember.Map} If options are passed, returns - `Ember.MapWithDefault` otherwise returns `Ember.Map` -*/ -MapWithDefault.create = function(options) { - if (options) { - return new MapWithDefault(options); - } else { - return new Map(); - } -}; - -MapWithDefault.prototype = Ember.create(Map.prototype); - -/** - Retrieve the value associated with a given key. - - @method get - @param {*} key - @return {*} the value associated with the key, or the default value -*/ -MapWithDefault.prototype.get = function(key) { - var hasValue = this.has(key); - - if (hasValue) { - return Map.prototype.get.call(this, key); - } else { - var defaultValue = this.defaultValue(key); - this.set(key, defaultValue); - return defaultValue; - } -}; - -/** - @method copy - @return {Ember.MapWithDefault} -*/ -MapWithDefault.prototype.copy = function() { - return copyMap(this, new MapWithDefault({ - defaultValue: this.defaultValue - })); -}; - -})(); - - - (function() { /** @module ember-metal @@ -2976,7 +2943,7 @@ var MANDATORY_SETTER = Ember.ENV.MANDATORY_SETTER; @private @constructor */ -Ember.Descriptor = function() {}; +var Descriptor = Ember.Descriptor = function() {}; // .......................................................... // DEFINING PROPERTIES API @@ -3537,7 +3504,7 @@ Ember.watchPath = function(obj, keyPath) { }; Ember.unwatchPath = function(obj, keyPath) { - var m = metaFor(obj), watching = m.watching; + var m = metaFor(obj), watching = m.watching, desc; if (watching[keyPath] === 1) { watching[keyPath] = 0; @@ -3799,14 +3766,27 @@ ComputedProperty.prototype = new Ember.Descriptor(); var ComputedPropertyPrototype = ComputedProperty.prototype; /* - Properties are cacheable by default. Computed property will automatically - cache the return value of your function until one of the dependent keys changes. + Call on a computed property to explicitly change it's cacheable mode. - Call `volatile()` to set it into non-cached mode. When in this mode - the computed property will not automatically cache the return value. + Please use `.volatile` over this method. - However, if a property is properly observable, there is no reason to disable - caching. + ```javascript + MyApp.president = Ember.Object.create({ + fullName: function() { + return this.get('firstName') + ' ' + this.get('lastName'); + + // By default, Ember will return the value of this property + // without re-executing this function. + }.property('firstName', 'lastName') + + initials: function() { + return this.get('firstName')[0] + this.get('lastName')[0]; + + // This function will be executed every time this property + // is requested. + }.property('firstName', 'lastName').cacheable(false) + }); + ``` @method cacheable @param {Boolean} aFlag optional set to `false` to disable caching @@ -4070,6 +4050,7 @@ ComputedPropertyPrototype.teardown = function(obj, keyName) { The function should accept two parameters, key and value. If value is not undefined you should set the value first. In either case return the current value of the property. + @method computed @for Ember @param {Function} func The computed property function. @@ -4311,7 +4292,7 @@ registerComputedWithProperties('or', function(properties) { @for Ember @param {String} dependentKey, [dependentKey...] @return {Ember.ComputedProperty} computed property which returns - the first truthy value of given list of properties. + the first trouthy value of given list of properties. */ registerComputedWithProperties('any', function(properties) { for (var key in properties) { @@ -4361,48 +4342,6 @@ Ember.computed.alias = function(dependentKey) { }); }; -/** - @method computed.oneWay - @for Ember - @param {String} dependentKey - @return {Ember.ComputedProperty} computed property which creates an - one way computed property to the original value for property. - - Where `computed.alias` aliases `get` and `set`, and allows for bidirectional - data flow, `computed.oneWay` only provides an aliased `get`. The `set` will - not mutate the upstream property, rather causes the current property to - become the value set. This causes the downstream property to permentantly - diverge from the upstream property. - - ```javascript - User = Ember.Object.extend({ - firstName: null, - lastName: null, - nickName: Ember.computed.oneWay('firstName') - }); - - user = User.create({ - firstName: 'Teddy', - lastName: 'Zeenny' - }); - - user.get('nickName'); - # 'Teddy' - - user.set('nickName', 'TeddyBear'); - # 'TeddyBear' - - user.get('firstName'); - # 'Teddy' - ``` -*/ -Ember.computed.oneWay = function(dependentKey) { - return Ember.computed(dependentKey, function() { - return get(this, dependentKey); - }); -}; - - /** @method computed.defaultTo @for Ember @@ -4432,6 +4371,8 @@ Ember.computed.defaultTo = function(defaultPath) { var AFTER_OBSERVERS = ':change'; var BEFORE_OBSERVERS = ':before'; +var guidFor = Ember.guidFor; + function changeEvent(keyName) { return keyName+AFTER_OBSERVERS; } @@ -4528,507 +4469,183 @@ Ember.removeBeforeObserver = function(obj, path, target, method) { (function() { -define("backburner", - ["backburner/deferred_action_queues","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var DeferredActionQueues = __dependency1__.DeferredActionQueues; +// Ember.Logger +// Ember.watch.flushPending +// Ember.beginPropertyChanges, Ember.endPropertyChanges +// Ember.guidFor, Ember.tryFinally - var slice = [].slice, - pop = [].pop, - debouncees = [], - timers = [], - autorun, laterTimer, laterTimerExpiresAt; +/** +@module ember-metal +*/ - function Backburner(queueNames, options) { - this.queueNames = queueNames; - this.options = options || {}; - if (!this.options.defaultQueue) { - this.options.defaultQueue = queueNames[0]; - } - this.instanceStack = []; +// .......................................................... +// HELPERS +// + +var slice = [].slice, + forEach = Ember.ArrayPolyfills.forEach; + +// invokes passed params - normalizing so you can pass target/func, +// target/string or just func +function invoke(target, method, args, ignore) { + + if (method === undefined) { + method = target; + target = undefined; + } + + if ('string' === typeof method) { method = target[method]; } + if (args && ignore > 0) { + args = args.length > ignore ? slice.call(args, ignore) : null; + } + + return Ember.handleErrors(function() { + // IE8's Function.prototype.apply doesn't accept undefined/null arguments. + return method.apply(target || this, args || []); + }, this); +} + + +// .......................................................... +// RUNLOOP +// + +/** +Ember RunLoop (Private) + +@class RunLoop +@namespace Ember +@private +@constructor +*/ +var RunLoop = function(prev) { + this._prev = prev || null; + this.onceTimers = {}; +}; + +RunLoop.prototype = { + /** + @method end + */ + end: function() { + this.flush(); + }, + + /** + @method prev + */ + prev: function() { + return this._prev; + }, + + // .......................................................... + // Delayed Actions + // + + /** + @method schedule + @param {String} queueName + @param target + @param method + */ + schedule: function(queueName, target, method) { + var queues = this._queues, queue; + if (!queues) { queues = this._queues = {}; } + queue = queues[queueName]; + if (!queue) { queue = queues[queueName] = []; } + + var args = arguments.length > 3 ? slice.call(arguments, 3) : null; + queue.push({ target: target, method: method, args: args }); + return this; + }, + + /** + @method flush + @param {String} queueName + */ + flush: function(queueName) { + var queueNames, idx, len, queue, log; + + if (!this._queues) { return this; } // nothing to do + + function iter(item) { + invoke(item.target, item.method, item.args); } - Backburner.prototype = { - queueNames: null, - options: null, - currentInstance: null, - instanceStack: null, + function tryable() { + forEach.call(queue, iter); + } - begin: function() { - var onBegin = this.options && this.options.onBegin, - previousInstance = this.currentInstance; + Ember.watch.flushPending(); // make sure all chained watchers are setup - if (previousInstance) { - this.instanceStack.push(previousInstance); + if (queueName) { + while (this._queues && (queue = this._queues[queueName])) { + this._queues[queueName] = null; + + // the sync phase is to allow property changes to propagate. don't + // invoke observers until that is finished. + if (queueName === 'sync') { + log = Ember.LOG_BINDINGS; + if (log) { Ember.Logger.log('Begin: Flush Sync Queue'); } + + Ember.beginPropertyChanges(); + + Ember.tryFinally(tryable, Ember.endPropertyChanges); + + if (log) { Ember.Logger.log('End: Flush Sync Queue'); } + + } else { + forEach.call(queue, iter); } + } - this.currentInstance = new DeferredActionQueues(this.queueNames, this.options); - if (onBegin) { - onBegin(this.currentInstance, previousInstance); - } - }, + } else { + queueNames = Ember.run.queues; + len = queueNames.length; + idx = 0; - end: function() { - var onEnd = this.options && this.options.onEnd, - currentInstance = this.currentInstance, - nextInstance = null; + outerloop: + while (idx < len) { + queueName = queueNames[idx]; + queue = this._queues && this._queues[queueName]; + delete this._queues[queueName]; - try { - currentInstance.flush(); - } finally { - this.currentInstance = null; + if (queue) { + // the sync phase is to allow property changes to propagate. don't + // invoke observers until that is finished. + if (queueName === 'sync') { + log = Ember.LOG_BINDINGS; + if (log) { Ember.Logger.log('Begin: Flush Sync Queue'); } - if (this.instanceStack.length) { - nextInstance = this.instanceStack.pop(); - this.currentInstance = nextInstance; - } + Ember.beginPropertyChanges(); - if (onEnd) { - onEnd(currentInstance, nextInstance); - } - } - }, + Ember.tryFinally(tryable, Ember.endPropertyChanges); - run: function(target, method /*, args */) { - var ret; - this.begin(); - - if (!method) { - method = target; - target = null; - } - - if (typeof method === 'string') { - method = target[method]; - } - - // Prevent Safari double-finally. - var finallyAlreadyCalled = false; - try { - if (arguments.length > 2) { - ret = method.apply(target, slice.call(arguments, 2)); + if (log) { Ember.Logger.log('End: Flush Sync Queue'); } } else { - ret = method.call(target); - } - } finally { - if (!finallyAlreadyCalled) { - finallyAlreadyCalled = true; - this.end(); + forEach.call(queue, iter); } } - return ret; - }, - defer: function(queueName, target, method /* , args */) { - if (!method) { - method = target; - target = null; - } - - if (typeof method === 'string') { - method = target[method]; - } - - var stack = this.DEBUG ? new Error().stack : undefined, - args = arguments.length > 3 ? slice.call(arguments, 3) : undefined; - if (!this.currentInstance) { createAutorun(this); } - return this.currentInstance.schedule(queueName, target, method, args, false, stack); - }, - - deferOnce: function(queueName, target, method /* , args */) { - if (!method) { - method = target; - target = null; - } - - if (typeof method === 'string') { - method = target[method]; - } - - var stack = this.DEBUG ? new Error().stack : undefined, - args = arguments.length > 3 ? slice.call(arguments, 3) : undefined; - if (!this.currentInstance) { createAutorun(this); } - return this.currentInstance.schedule(queueName, target, method, args, true, stack); - }, - - setTimeout: function() { - var self = this, - wait = pop.call(arguments), - target = arguments[0], - method = arguments[1], - executeAt = (+new Date()) + wait; - - if (!method) { - method = target; - target = null; - } - - if (typeof method === 'string') { - method = target[method]; - } - - var fn, args; - if (arguments.length > 2) { - args = slice.call(arguments, 2); - - fn = function() { - method.apply(target, args); - }; - } else { - fn = function() { - method.call(target); - }; - } - - // find position to insert - TODO: binary search - var i, l; - for (i = 0, l = timers.length; i < l; i += 2) { - if (executeAt < timers[i]) { break; } - } - - timers.splice(i, 0, executeAt, fn); - - if (laterTimer && laterTimerExpiresAt < executeAt) { return fn; } - - if (laterTimer) { - clearTimeout(laterTimer); - laterTimer = null; - } - laterTimer = window.setTimeout(function() { - executeTimers(self); - laterTimer = null; - laterTimerExpiresAt = null; - }, wait); - laterTimerExpiresAt = executeAt; - - return fn; - }, - - debounce: function(target, method /* , args, wait */) { - var self = this, - args = arguments, - wait = pop.call(args), - debouncee; - - for (var i = 0, l = debouncees.length; i < l; i++) { - debouncee = debouncees[i]; - if (debouncee[0] === target && debouncee[1] === method) { return; } // do nothing - } - - var timer = window.setTimeout(function() { - self.run.apply(self, args); - - // remove debouncee - var index = -1; - for (var i = 0, l = debouncees.length; i < l; i++) { - debouncee = debouncees[i]; - if (debouncee[0] === target && debouncee[1] === method) { - index = i; - break; - } - } - - if (index > -1) { debouncees.splice(index, 1); } - }, wait); - - debouncees.push([target, method, timer]); - }, - - cancelTimers: function() { - for (var i = 0, l = debouncees.length; i < l; i++) { - clearTimeout(debouncees[i][2]); - } - debouncees = []; - - if (laterTimer) { - clearTimeout(laterTimer); - laterTimer = null; - } - timers = []; - - if (autorun) { - clearTimeout(autorun); - autorun = null; - } - }, - - hasTimers: function() { - return !!timers.length || autorun; - }, - - cancel: function(timer) { - if (typeof timer === 'object' && timer.queue && timer.method) { // we're cancelling a deferOnce - return timer.queue.cancel(timer); - } else if (typeof timer === 'function') { // we're cancelling a setTimeout - for (var i = 0, l = timers.length; i < l; i += 2) { - if (timers[i + 1] === timer) { - timers.splice(i, 2); // remove the two elements - return true; - } - } - } - } - }; - - Backburner.prototype.schedule = Backburner.prototype.defer; - Backburner.prototype.scheduleOnce = Backburner.prototype.deferOnce; - Backburner.prototype.later = Backburner.prototype.setTimeout; - - function createAutorun(backburner) { - backburner.begin(); - autorun = window.setTimeout(function() { - backburner.end(); - autorun = null; - }); - } - - function executeTimers(self) { - var now = +new Date(), - time, fns, i, l; - - self.run(function() { - // TODO: binary search - for (i = 0, l = timers.length; i < l; i += 2) { - time = timers[i]; - if (time > now) { break; } - } - - fns = timers.splice(0, i); - - for (i = 1, l = fns.length; i < l; i += 2) { - self.schedule(self.options.defaultQueue, null, fns[i]); - } - }); - - if (timers.length) { - laterTimer = window.setTimeout(function() { - executeTimers(self); - laterTimer = null; - laterTimerExpiresAt = null; - }, timers[0] - now); - laterTimerExpiresAt = timers[0]; - } - } - - - __exports__.Backburner = Backburner; - }); - -define("backburner/deferred_action_queues", - ["backburner/queue","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var Queue = __dependency1__.Queue; - - function DeferredActionQueues(queueNames, options) { - var queues = this.queues = {}; - this.queueNames = queueNames = queueNames || []; - - var queueName; - for (var i = 0, l = queueNames.length; i < l; i++) { - queueName = queueNames[i]; - queues[queueName] = new Queue(this, queueName, options[queueName]); - } - } - - DeferredActionQueues.prototype = { - queueNames: null, - queues: null, - - schedule: function(queueName, target, method, args, onceFlag, stack) { - var queues = this.queues, - queue = queues[queueName]; - - if (!queue) { throw new Error("You attempted to schedule an action in a queue (" + queueName + ") that doesn't exist"); } - - if (onceFlag) { - return queue.pushUnique(target, method, args, stack); - } else { - return queue.push(target, method, args, stack); - } - }, - - flush: function() { - var queues = this.queues, - queueNames = this.queueNames, - queueName, queue, queueItems, priorQueueNameIndex, - queueNameIndex = 0, numberOfQueues = queueNames.length; - - outerloop: - while (queueNameIndex < numberOfQueues) { - queueName = queueNames[queueNameIndex]; - queue = queues[queueName]; - queueItems = queue._queue.slice(); - queue._queue = []; - - var options = queue.options, - before = options && options.before, - after = options && options.after, - target, method, args, stack, - queueIndex = 0, numberOfQueueItems = queueItems.length; - - if (numberOfQueueItems && before) { before(); } - while (queueIndex < numberOfQueueItems) { - target = queueItems[queueIndex]; - method = queueItems[queueIndex+1]; - args = queueItems[queueIndex+2]; - stack = queueItems[queueIndex+3]; // Debugging assistance - - if (typeof method === 'string') { method = target[method]; } - - // TODO: error handling - if (args && args.length > 0) { - method.apply(target, args); - } else { - method.call(target); - } - - queueIndex += 4; - } - if (numberOfQueueItems && after) { after(); } - - if ((priorQueueNameIndex = indexOfPriorQueueWithActions(this, queueNameIndex)) !== -1) { - queueNameIndex = priorQueueNameIndex; + // Loop through prior queues + for (var i = 0; i <= idx; i++) { + if (this._queues && this._queues[queueNames[i]]) { + // Start over at the first queue with contents + idx = i; continue outerloop; } - - queueNameIndex++; } + + idx++; } - }; - - function indexOfPriorQueueWithActions(daq, currentQueueIndex) { - var queueName, queue; - - for (var i = 0, l = currentQueueIndex; i <= l; i++) { - queueName = daq.queueNames[i]; - queue = daq.queues[queueName]; - if (queue._queue.length) { return i; } - } - - return -1; } - __exports__.DeferredActionQueues = DeferredActionQueues; - }); + return this; + } -define("backburner/queue", - ["exports"], - function(__exports__) { - "use strict"; - function Queue(daq, name, options) { - this.daq = daq; - this.name = name; - this.options = options; - this._queue = []; - } - - Queue.prototype = { - daq: null, - name: null, - options: null, - _queue: null, - - push: function(target, method, args, stack) { - var queue = this._queue; - queue.push(target, method, args, stack); - return {queue: this, target: target, method: method}; - }, - - pushUnique: function(target, method, args, stack) { - var queue = this._queue, currentTarget, currentMethod, i, l; - - for (i = 0, l = queue.length; i < l; i += 4) { - currentTarget = queue[i]; - currentMethod = queue[i+1]; - - if (currentTarget === target && currentMethod === method) { - queue[i+2] = args; // replace args - queue[i+3] = stack; // replace stack - return {queue: this, target: target, method: method}; // TODO: test this code path - } - } - - this._queue.push(target, method, args, stack); - return {queue: this, target: target, method: method}; - }, - - // TODO: remove me, only being used for Ember.run.sync - flush: function() { - var queue = this._queue, - options = this.options, - before = options && options.before, - after = options && options.after, - target, method, args, stack, i, l = queue.length; - - if (l && before) { before(); } - for (i = 0; i < l; i += 4) { - target = queue[i]; - method = queue[i+1]; - args = queue[i+2]; - stack = queue[i+3]; // Debugging assistance - - // TODO: error handling - if (args && args.length > 0) { - method.apply(target, args); - } else { - method.call(target); - } - } - if (l && after) { after(); } - - // check if new items have been added - if (queue.length > l) { - this._queue = queue.slice(l); - this.flush(); - } else { - this._queue.length = 0; - } - }, - - cancel: function(actionToCancel) { - var queue = this._queue, currentTarget, currentMethod, i, l; - - for (i = 0, l = queue.length; i < l; i += 4) { - currentTarget = queue[i]; - currentMethod = queue[i+1]; - - if (currentTarget === actionToCancel.target && currentMethod === actionToCancel.method) { - queue.splice(i, 4); - return true; - } - } - } - }; - - __exports__.Queue = Queue; - }); - -})(); - - - -(function() { -var onBegin = function(current) { - Ember.run.currentRunLoop = current; }; -var onEnd = function(current, next) { - Ember.run.currentRunLoop = next; -}; - -var Backburner = requireModule('backburner').Backburner, - backburner = new Backburner(['sync', 'actions', 'destroy'], { - sync: { - before: Ember.beginPropertyChanges, - after: Ember.endPropertyChanges - }, - defaultQueue: 'actions', - onBegin: onBegin, - onEnd: onEnd - }), - slice = [].slice; +Ember.RunLoop = RunLoop; // .......................................................... // Ember.run - this is ideally the only public API the dev sees @@ -5062,76 +4679,20 @@ var Backburner = requireModule('backburner').Backburner, @return {Object} return value from invoking the passed function. */ Ember.run = function(target, method) { - var ret; + var args = arguments; + run.begin(); - if (Ember.onerror) { - try { - ret = backburner.run.apply(backburner, arguments); - } catch (e) { - Ember.onerror(e); + function tryable() { + if (target || method) { + return invoke(target, method, args, 2); } - } else { - ret = backburner.run.apply(backburner, arguments); } - return ret; + return Ember.tryFinally(tryable, run.end); }; -/** - - If no run-loop is present, it creates a new one. If a run loop is - present it will queue itself to run on the existing run-loops action - queue. - - Please note: This is not for normal usage, and should be used sparingly. - - If invoked when not within a run loop: - - ```javascript - Ember.run.join(function(){ - // creates a new run-loop - }); - ``` - - Alternatively, if called within an existing run loop: - - ```javascript - Ember.run(function(){ - // creates a new run-loop - Ember.run.join(function(){ - // joins with the existing run-loop, and queues for invocation on - // the existing run-loops action queue. - }); - }); - ``` - - @method join - @namespace Ember - @param {Object} [target] target of method to call - @param {Function|String} method Method to invoke. - May be a function or a string. If you pass a string - then it will be looked up on the passed target. - @param {Object} [args*] Any additional arguments you wish to pass to the method. - @return {Object} return value from invoking the passed function. Please note, - when called within an existing loop, no return value is possible. -*/ -Ember.run.join = function(target, method) { - if (!Ember.run.currentRunLoop) { - return Ember.run.apply(Ember.run, arguments); - } - - var args = slice.call(arguments); - args.unshift('actions'); - Ember.run.schedule.apply(Ember.run, args); -}; - -Ember.run.backburner = backburner; - var run = Ember.run; -Ember.run.currentRunLoop = null; - -Ember.run.queues = backburner.queueNames; /** Begins a new RunLoop. Any deferred actions invoked after the begin will @@ -5148,7 +4709,7 @@ Ember.run.queues = backburner.queueNames; @return {void} */ Ember.run.begin = function() { - backburner.begin(); + run.currentRunLoop = new RunLoop(run.currentRunLoop); }; /** @@ -5166,7 +4727,12 @@ Ember.run.begin = function() { @return {void} */ Ember.run.end = function() { - backburner.end(); + Ember.assert('must have a current run loop', run.currentRunLoop); + + function tryable() { run.currentRunLoop.end(); } + function finalizer() { run.currentRunLoop = run.currentRunLoop.prev(); } + + Ember.tryFinally(tryable, finalizer); }; /** @@ -5179,6 +4745,7 @@ Ember.run.end = function() { @type Array @default ['sync', 'actions', 'destroy'] */ +Ember.run.queues = ['sync', 'actions', 'destroy']; /** Adds the passed target/method and any optional arguments to the named @@ -5217,18 +4784,57 @@ Ember.run.end = function() { @return {void} */ Ember.run.schedule = function(queue, target, method) { - checkAutoRun(); - backburner.schedule.apply(backburner, arguments); + var loop = run.autorun(); + loop.schedule.apply(loop, arguments); }; +var scheduledAutorun; +function autorun() { + scheduledAutorun = null; + if (run.currentRunLoop) { run.end(); } +} + // Used by global test teardown Ember.run.hasScheduledTimers = function() { - return backburner.hasTimers(); + return !!(scheduledAutorun || scheduledLater); }; // Used by global test teardown Ember.run.cancelTimers = function () { - backburner.cancelTimers(); + if (scheduledAutorun) { + clearTimeout(scheduledAutorun); + scheduledAutorun = null; + } + if (scheduledLater) { + clearTimeout(scheduledLater); + scheduledLater = null; + } + timers = {}; +}; + +/** + Begins a new RunLoop if necessary and schedules a timer to flush the + RunLoop at a later time. This method is used by parts of Ember to + ensure the RunLoop always finishes. You normally do not need to call this + method directly. Instead use `Ember.run()` + + @method autorun + @example + Ember.run.autorun(); + @return {Ember.RunLoop} the new current RunLoop +*/ +Ember.run.autorun = function() { + if (!run.currentRunLoop) { + Ember.assert("You have turned on testing mode, which disabled the run-loop's autorun. You will need to wrap any code with asynchronous side-effects in an Ember.run", !Ember.testing); + + run.begin(); + + if (!scheduledAutorun) { + scheduledAutorun = setTimeout(autorun, 1); + } + } + + return run.currentRunLoop; }; /** @@ -5248,9 +4854,42 @@ Ember.run.cancelTimers = function () { @return {void} */ Ember.run.sync = function() { - backburner.currentInstance.queues.sync.flush(); + run.autorun(); + run.currentRunLoop.flush('sync'); }; +// .......................................................... +// TIMERS +// + +var timers = {}; // active timers... + +var scheduledLater, scheduledLaterExpires; +function invokeLaterTimers() { + scheduledLater = null; + run(function() { + var now = (+ new Date()), earliest = -1; + for (var key in timers) { + if (!timers.hasOwnProperty(key)) { continue; } + var timer = timers[key]; + if (timer && timer.expires) { + if (now >= timer.expires) { + delete timers[key]; + invoke(timer.target, timer.method, timer.args, 2); + } else { + if (earliest < 0 || (timer.expires < earliest)) { earliest = timer.expires; } + } + } + } + + // schedule next timeout to fire when the earliest timer expires + if (earliest > 0) { + scheduledLater = setTimeout(invokeLaterTimers, earliest - now); + scheduledLaterExpires = earliest; + } + }); +} + /** Invokes the passed target/method and optional arguments after a specified period if time. The last parameter of this method must always be a number @@ -5275,12 +4914,77 @@ Ember.run.sync = function() { @param {Object} [args*] Optional arguments to pass to the timeout. @param {Number} wait Number of milliseconds to wait. @return {String} a string you can use to cancel the timer in - `Ember.run.cancel` later. + {{#crossLink "Ember/run.cancel"}}{{/crossLink}} later. */ Ember.run.later = function(target, method) { - return backburner.later.apply(backburner, arguments); + var args, expires, timer, guid, wait; + + // setTimeout compatibility... + if (arguments.length===2 && 'function' === typeof target) { + wait = method; + method = target; + target = undefined; + args = [target, method]; + } else { + args = slice.call(arguments); + wait = args.pop(); + } + + expires = (+ new Date()) + wait; + timer = { target: target, method: method, expires: expires, args: args }; + guid = Ember.guidFor(timer); + timers[guid] = timer; + + if(scheduledLater && expires < scheduledLaterExpires) { + // Cancel later timer (then reschedule earlier timer below) + clearTimeout(scheduledLater); + scheduledLater = null; + } + + if (!scheduledLater) { + // Schedule later timers to be run. + scheduledLater = setTimeout(invokeLaterTimers, wait); + scheduledLaterExpires = expires; + } + + return guid; }; +function invokeOnceTimer(guid, onceTimers) { + if (onceTimers[this.tguid]) { delete onceTimers[this.tguid][this.mguid]; } + if (timers[guid]) { invoke(this.target, this.method, this.args); } + delete timers[guid]; +} + +function scheduleOnce(queue, target, method, args) { + var tguid = Ember.guidFor(target), + mguid = Ember.guidFor(method), + onceTimers = run.autorun().onceTimers, + guid = onceTimers[tguid] && onceTimers[tguid][mguid], + timer; + + if (guid && timers[guid]) { + timers[guid].args = args; // replace args + } else { + timer = { + target: target, + method: method, + args: args, + tguid: tguid, + mguid: mguid + }; + + guid = Ember.guidFor(timer); + timers[guid] = timer; + if (!onceTimers[tguid]) { onceTimers[tguid] = {}; } + onceTimers[tguid][mguid] = guid; // so it isn't scheduled more than once + + run.schedule(queue, timer, invokeOnceTimer, guid, onceTimers); + } + + return guid; +} + /** Schedule a function to run one time during the current RunLoop. This is equivalent to calling `scheduleOnce` with the "actions" queue. @@ -5294,10 +4998,7 @@ Ember.run.later = function(target, method) { @return {Object} timer */ Ember.run.once = function(target, method) { - checkAutoRun(); - var args = slice.call(arguments); - args.unshift('actions'); - return backburner.scheduleOnce.apply(backburner, args); + return scheduleOnce('actions', target, method, slice.call(arguments, 2)); }; /** @@ -5345,8 +5046,7 @@ Ember.run.once = function(target, method) { @return {Object} timer */ Ember.run.scheduleOnce = function(queue, target, method) { - checkAutoRun(); - return backburner.scheduleOnce.apply(backburner, arguments); + return scheduleOnce(queue, target, method, slice.call(arguments, 3)); }; /** @@ -5408,8 +5108,8 @@ Ember.run.scheduleOnce = function(queue, target, method) { */ Ember.run.next = function() { var args = slice.call(arguments); - args.push(1); - return backburner.later.apply(backburner, args); + args.push(1); // 1 millisecond wait + return run.later.apply(this, args); }; /** @@ -5438,16 +5138,9 @@ Ember.run.next = function() { @return {void} */ Ember.run.cancel = function(timer) { - return backburner.cancel(timer); + delete timers[timer]; }; -// Make sure it's not an autorun during testing -function checkAutoRun() { - if (!Ember.run.currentRunLoop) { - Ember.assert("You have turned on testing mode, which disabled the run-loop's autorun. You will need to wrap any code with asynchronous side-effects in an Ember.run", !Ember.testing); - } -} - })(); @@ -5732,7 +5425,7 @@ function mixinProperties(to, from) { mixinProperties(Binding, { /** - See `Ember.Binding.from`. + See {{#crossLink "Ember.Binding/from"}}{{/crossLink}} @method from @static @@ -5743,7 +5436,7 @@ mixinProperties(Binding, { }, /** - See `Ember.Binding.to`. + See {{#crossLink "Ember.Binding/to"}}{{/crossLink}} @method to @static @@ -5760,7 +5453,7 @@ mixinProperties(Binding, { This means that if you change the "to" side directly, the "from" side may have a different value. - See `Binding.oneWay`. + See {{#crossLink "Binding/oneWay"}}{{/crossLink}} @method oneWay @param {String} from from path. @@ -5821,7 +5514,7 @@ mixinProperties(Binding, { You should consider using one way bindings anytime you have an object that may be created frequently and you do not intend to change a property; only - to monitor it for changes (such as in the example above). + to monitor it for changes. (such as in the example above). ## Adding Bindings Manually @@ -6122,6 +5815,14 @@ function mergeMixins(mixins, m, descs, values, base, keys) { } } +function writableReq(obj) { + var m = Ember.meta(obj), req = m.required; + if (!req || !m.hasOwnProperty('required')) { + req = m.required = req ? o_create(req) : {}; + } + return req; +} + var IS_BINDING = Ember.IS_BINDING = /^.+Binding$/; function detectBinding(obj, key, value, m) { @@ -6580,29 +6281,6 @@ Ember.immediateObserver = function() { }; /** - When observers fire, they are called with the arguments `obj`, `keyName` - and `value`. In a typical observer, value is the new, post-change value. - - A `beforeObserver` fires before a property changes. The `value` argument contains - the pre-change value. - - A `beforeObserver` is an alternative form of `.observesBefore()`. - - ```javascript - App.PersonView = Ember.View.extend({ - valueWillChange: function (obj, keyName, value) { - this.changingFrom = value; - }.observesBefore('content.value'), - valueDidChange: function(obj, keyName, value) { - // only run if updating a value already in the DOM - if(this.get('state') === 'inDOM') { - var color = value > this.changingFrom ? 'green' : 'red'; - // logic - } - }.observes('content.value') - }); - ``` - @method beforeObserver @for Ember @param {Function} func @@ -6630,57 +6308,14 @@ Ember Metal })(); (function() { -define("rsvp/all", - ["rsvp/defer","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var defer = __dependency1__.defer; - - function all(promises) { - var results = [], deferred = defer(), remaining = promises.length; - - if (remaining === 0) { - deferred.resolve([]); - } - - var resolver = function(index) { - return function(value) { - resolveAll(index, value); - }; - }; - - var resolveAll = function(index, value) { - results[index] = value; - if (--remaining === 0) { - deferred.resolve(results); - } - }; - - var rejectAll = function(error) { - deferred.reject(error); - }; - - for (var i = 0; i < promises.length; i++) { - if (promises[i] && typeof promises[i].then === 'function') { - promises[i].then(resolver(i), rejectAll); - } else { - resolveAll(i, promises[i]); - } - } - return deferred.promise; - } - - __exports__.all = all; - }); - -define("rsvp/async", - ["exports"], - function(__exports__) { +define("rsvp", + [], + function() { "use strict"; var browserGlobal = (typeof window !== 'undefined') ? window : {}; - var BrowserMutationObserver = browserGlobal.MutationObserver || browserGlobal.WebKitMutationObserver; - var async; + var MutationObserver = browserGlobal.MutationObserver || browserGlobal.WebKitMutationObserver; + var RSVP, async; if (typeof process !== 'undefined' && {}.toString.call(process) === '[object process]') { @@ -6689,10 +6324,10 @@ define("rsvp/async", callback.call(binding); }); }; - } else if (BrowserMutationObserver) { + } else if (MutationObserver) { var queue = []; - var observer = new BrowserMutationObserver(function() { + var observer = new MutationObserver(function() { var toProcess = queue.slice(); queue = []; @@ -6723,47 +6358,6 @@ define("rsvp/async", }; } - - __exports__.async = async; - }); - -define("rsvp/config", - ["rsvp/async","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var async = __dependency1__.async; - - var config = {}; - config.async = async; - - __exports__.config = config; - }); - -define("rsvp/defer", - ["rsvp/promise","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var Promise = __dependency1__.Promise; - - function defer() { - var deferred = {}; - - var promise = new Promise(function(resolve, reject) { - deferred.resolve = resolve; - deferred.reject = reject; - }); - - deferred.promise = promise; - return deferred; - } - - __exports__.defer = defer; - }); - -define("rsvp/events", - ["exports"], - function(__exports__) { - "use strict"; var Event = function(type, options) { this.type = type; @@ -6858,148 +6452,7 @@ define("rsvp/events", } }; - - __exports__.EventTarget = EventTarget; - }); - -define("rsvp/hash", - ["rsvp/defer","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var defer = __dependency1__.defer; - - function size(object) { - var size = 0; - - for (var prop in object) { - size++; - } - - return size; - } - - function hash(promises) { - var results = {}, deferred = defer(), remaining = size(promises); - - if (remaining === 0) { - deferred.resolve({}); - } - - var resolver = function(prop) { - return function(value) { - resolveAll(prop, value); - }; - }; - - var resolveAll = function(prop, value) { - results[prop] = value; - if (--remaining === 0) { - deferred.resolve(results); - } - }; - - var rejectAll = function(error) { - deferred.reject(error); - }; - - for (var prop in promises) { - if (promises[prop] && typeof promises[prop].then === 'function') { - promises[prop].then(resolver(prop), rejectAll); - } else { - resolveAll(prop, promises[prop]); - } - } - - return deferred.promise; - } - - __exports__.hash = hash; - }); - -define("rsvp/node", - ["rsvp/promise","rsvp/all","exports"], - function(__dependency1__, __dependency2__, __exports__) { - "use strict"; - var Promise = __dependency1__.Promise; - var all = __dependency2__.all; - - function makeNodeCallbackFor(resolve, reject) { - return function (error, value) { - if (error) { - reject(error); - } else if (arguments.length > 2) { - resolve(Array.prototype.slice.call(arguments, 1)); - } else { - resolve(value); - } - }; - } - - function denodeify(nodeFunc) { - return function() { - var nodeArgs = Array.prototype.slice.call(arguments), resolve, reject; - - var promise = new Promise(function(nodeResolve, nodeReject) { - resolve = nodeResolve; - reject = nodeReject; - }); - - all(nodeArgs).then(function(nodeArgs) { - nodeArgs.push(makeNodeCallbackFor(resolve, reject)); - - try { - nodeFunc.apply(this, nodeArgs); - } catch(e) { - reject(e); - } - }); - - return promise; - }; - } - - __exports__.denodeify = denodeify; - }); - -define("rsvp/promise", - ["rsvp/config","rsvp/events","exports"], - function(__dependency1__, __dependency2__, __exports__) { - "use strict"; - var config = __dependency1__.config; - var EventTarget = __dependency2__.EventTarget; - - function objectOrFunction(x) { - return isFunction(x) || (typeof x === "object" && x !== null); - } - - function isFunction(x){ - return typeof x === "function"; - } - - var Promise = function(resolver) { - var promise = this, - resolved = false; - - if (typeof resolver !== 'function') { - throw new TypeError('You must pass a resolver function as the sole argument to the promise constructor'); - } - - if (!(promise instanceof Promise)) { - return new Promise(resolver); - } - - var resolvePromise = function(value) { - if (resolved) { return; } - resolved = true; - resolve(promise, value); - }; - - var rejectPromise = function(value) { - if (resolved) { return; } - resolved = true; - reject(promise, value); - }; - + var Promise = function() { this.on('promise:resolved', function(event) { this.trigger('success', { detail: event.detail }); }, this); @@ -7007,16 +6460,12 @@ define("rsvp/promise", this.on('promise:failed', function(event) { this.trigger('error', { detail: event.detail }); }, this); - - try { - resolver(resolvePromise, rejectPromise); - } catch(e) { - rejectPromise(e); - } }; + var noop = function() {}; + var invokeCallback = function(type, promise, callback, event) { - var hasCallback = isFunction(callback), + var hasCallback = typeof callback === 'function', value, error, succeeded, failed; if (hasCallback) { @@ -7032,34 +6481,34 @@ define("rsvp/promise", succeeded = true; } - if (handleThenable(promise, value)) { - return; + if (value && typeof value.then === 'function') { + value.then(function(value) { + promise.resolve(value); + }, function(error) { + promise.reject(error); + }); } else if (hasCallback && succeeded) { - resolve(promise, value); + promise.resolve(value); } else if (failed) { - reject(promise, error); - } else if (type === 'resolve') { - resolve(promise, value); - } else if (type === 'reject') { - reject(promise, value); + promise.reject(error); + } else { + promise[type](value); } }; Promise.prototype = { - constructor: Promise, - then: function(done, fail) { - var thenPromise = new Promise(function() {}); + var thenPromise = new Promise(); - if (this.isFulfilled) { - config.async(function() { - invokeCallback('resolve', thenPromise, done, { detail: this.fulfillmentValue }); + if (this.isResolved) { + RSVP.async(function() { + invokeCallback('resolve', thenPromise, done, { detail: this.resolvedValue }); }, this); } if (this.isRejected) { - config.async(function() { - invokeCallback('reject', thenPromise, fail, { detail: this.rejectedReason }); + RSVP.async(function() { + invokeCallback('reject', thenPromise, fail, { detail: this.rejectedValue }); }, this); } @@ -7072,162 +6521,75 @@ define("rsvp/promise", }); return thenPromise; + }, + + resolve: function(value) { + resolve(this, value); + + this.resolve = noop; + this.reject = noop; + }, + + reject: function(value) { + reject(this, value); + + this.resolve = noop; + this.reject = noop; } }; - EventTarget.mixin(Promise.prototype); - function resolve(promise, value) { - if (promise === value) { - fulfill(promise, value); - } else if (!handleThenable(promise, value)) { - fulfill(promise, value); - } - } - - function handleThenable(promise, value) { - var then = null; - - if (objectOrFunction(value)) { - try { - then = value.then; - } catch(e) { - reject(promise, e); - return true; - } - - if (isFunction(then)) { - try { - then.call(value, function(val) { - if (value !== val) { - resolve(promise, val); - } else { - fulfill(promise, val); - } - }, function(val) { - reject(promise, val); - }); - } catch (e) { - reject(promise, e); - } - return true; - } - } - - return false; - } - - function fulfill(promise, value) { - config.async(function() { + RSVP.async(function() { promise.trigger('promise:resolved', { detail: value }); - promise.isFulfilled = true; - promise.fulfillmentValue = value; + promise.isResolved = true; + promise.resolvedValue = value; }); } function reject(promise, value) { - config.async(function() { + RSVP.async(function() { promise.trigger('promise:failed', { detail: value }); promise.isRejected = true; - promise.rejectedReason = value; + promise.rejectedValue = value; }); } + function all(promises) { + var i, results = []; + var allPromise = new Promise(); + var remaining = promises.length; - __exports__.Promise = Promise; - }); + if (remaining === 0) { + allPromise.resolve([]); + } -define("rsvp/reject", - ["rsvp/promise","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var Promise = __dependency1__.Promise; + var resolver = function(index) { + return function(value) { + resolve(index, value); + }; + }; - - function objectOrFunction(x) { - return typeof x === "function" || (typeof x === "object" && x !== null); - } - - - function reject(reason) { - return new Promise(function (resolve, reject) { - reject(reason); - }); - } - - - __exports__.reject = reject; - }); - -define("rsvp/resolve", - ["rsvp/promise","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var Promise = __dependency1__.Promise; - - - function objectOrFunction(x) { - return typeof x === "function" || (typeof x === "object" && x !== null); - } - - function resolve(thenable){ - var promise = new Promise(function(resolve, reject){ - var then; - - try { - if ( objectOrFunction(thenable) ) { - then = thenable.then; - - if (typeof then === "function") { - then.call(thenable, resolve, reject); - } else { - resolve(thenable); - } - - } else { - resolve(thenable); - } - - } catch(error) { - reject(error); + var resolve = function(index, value) { + results[index] = value; + if (--remaining === 0) { + allPromise.resolve(results); } - }); + }; - return promise; + var reject = function(error) { + allPromise.reject(error); + }; + + for (i = 0; i < remaining; i++) { + promises[i].then(resolver(i), reject); + } + return allPromise; } + EventTarget.mixin(Promise.prototype); - __exports__.resolve = resolve; - }); - -define("rsvp", - ["rsvp/events","rsvp/promise","rsvp/node","rsvp/all","rsvp/hash","rsvp/defer","rsvp/config","rsvp/resolve","rsvp/reject","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __exports__) { - "use strict"; - var EventTarget = __dependency1__.EventTarget; - var Promise = __dependency2__.Promise; - var denodeify = __dependency3__.denodeify; - var all = __dependency4__.all; - var hash = __dependency5__.hash; - var defer = __dependency6__.defer; - var config = __dependency7__.config; - var resolve = __dependency8__.resolve; - var reject = __dependency9__.reject; - - function configure(name, value) { - config[name] = value; - } - - - __exports__.Promise = Promise; - __exports__.EventTarget = EventTarget; - __exports__.all = all; - __exports__.hash = hash; - __exports__.defer = defer; - __exports__.denodeify = denodeify; - __exports__.configure = configure; - __exports__.resolve = resolve; - __exports__.reject = reject; + RSVP = { async: async, Promise: Promise, Event: Event, EventTarget: EventTarget, all: all, raiseOnUncaughtExceptions: true }; + return RSVP; }); })(); @@ -7403,6 +6765,10 @@ define("container", this.children = []; + eachDestroyable(this, function(item) { + item.isDestroying = true; + }); + eachDestroyable(this, function(item) { item.destroy(); }); @@ -7774,15 +7140,10 @@ Ember.ORDER_DEFINITION = Ember.ENV.ORDER_DEFINITION || [ */ Ember.keys = Object.keys; -if (!Ember.keys || Ember.create.isSimulated) { +if (!Ember.keys) { Ember.keys = function(obj) { var ret = []; for(var key in obj) { - // Prevents browsers that don't respect non-enumerability from - // copying internal Ember properties - if (key.substring(0,2) === '__') continue; - if (key === '_super') continue; - if (obj.hasOwnProperty(key)) { ret.push(key); } } return ret; @@ -7804,7 +7165,7 @@ var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'n @constructor */ Ember.Error = function() { - var tmp = Error.apply(this, arguments); + var tmp = Error.prototype.constructor.apply(this, arguments); // Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work. for (var idx = 0; idx < errorProps.length; idx++) { @@ -8066,12 +7427,10 @@ Ember.String = { /** Returns the Capitalized form of a string - ```javascript - 'innerHTML'.capitalize() // 'InnerHTML' - 'action_name'.capitalize() // 'Action_name' - 'css-class-name'.capitalize() // 'Css-class-name' - 'my favorite items'.capitalize() // 'My favorite items' - ``` + 'innerHTML'.capitalize() // 'InnerHTML' + 'action_name'.capitalize() // 'Action_name' + 'css-class-name'.capitalize() // 'Css-class-name' + 'my favorite items'.capitalize() // 'My favorite items' @method capitalize @param {String} str @@ -8108,7 +7467,7 @@ var fmt = Ember.String.fmt, if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.String) { /** - See `Ember.String.fmt`. + See {{#crossLink "Ember.String/fmt"}}{{/crossLink}} @method fmt @for String @@ -8118,7 +7477,7 @@ if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.String) { }; /** - See `Ember.String.w`. + See {{#crossLink "Ember.String/w"}}{{/crossLink}} @method w @for String @@ -8128,7 +7487,7 @@ if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.String) { }; /** - See `Ember.String.loc`. + See {{#crossLink "Ember.String/loc"}}{{/crossLink}} @method loc @for String @@ -8138,7 +7497,7 @@ if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.String) { }; /** - See `Ember.String.camelize`. + See {{#crossLink "Ember.String/camelize"}}{{/crossLink}} @method camelize @for String @@ -8148,7 +7507,7 @@ if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.String) { }; /** - See `Ember.String.decamelize`. + See {{#crossLink "Ember.String/decamelize"}}{{/crossLink}} @method decamelize @for String @@ -8158,7 +7517,7 @@ if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.String) { }; /** - See `Ember.String.dasherize`. + See {{#crossLink "Ember.String/dasherize"}}{{/crossLink}} @method dasherize @for String @@ -8168,7 +7527,7 @@ if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.String) { }; /** - See `Ember.String.underscore`. + See {{#crossLink "Ember.String/underscore"}}{{/crossLink}} @method underscore @for String @@ -8178,7 +7537,7 @@ if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.String) { }; /** - See `Ember.String.classify`. + See {{#crossLink "Ember.String/classify"}}{{/crossLink}} @method classify @for String @@ -8188,7 +7547,7 @@ if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.String) { }; /** - See `Ember.String.capitalize`. + See {{#crossLink "Ember.String/capitalize"}}{{/crossLink}} @method capitalize @for String @@ -8264,7 +7623,8 @@ if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.Function) { will instead clear the cache so that it is updated when the next `get` is called on the property. - See `Ember.ComputedProperty`, `Ember.computed`. + See {{#crossLink "Ember.ComputedProperty"}}{{/crossLink}}, + {{#crossLink "Ember/computed"}}{{/crossLink}} @method property @for Function @@ -8291,7 +7651,7 @@ if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.Function) { }); ``` - See `Ember.Observable.observes`. + See {{#crossLink "Ember.Observable/observes"}}{{/crossLink}} @method observes @for Function @@ -8318,7 +7678,7 @@ if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.Function) { }); ``` - See `Ember.Observable.observesBefore`. + See {{#crossLink "Ember.Observable/observesBefore"}}{{/crossLink}} @method observesBefore @for Function @@ -9215,7 +8575,9 @@ Ember.Enumerable = Ember.Mixin.create({ // HELPERS // -var get = Ember.get, set = Ember.set, isNone = Ember.isNone, map = Ember.EnumerableUtils.map, cacheFor = Ember.cacheFor; +var get = Ember.get, set = Ember.set, map = Ember.EnumerableUtils.map, cacheFor = Ember.cacheFor; + +function none(obj) { return obj===null || obj===undefined; } // .......................................................... // ARRAY @@ -9238,7 +8600,7 @@ var get = Ember.get, set = Ember.set, isNone = Ember.isNone, map = Ember.Enumera You can use the methods defined in this module to access and modify array contents in a KVO-friendly way. You can also be notified whenever the - membership of an array changes by changing the syntax of the property to + membership if an array changes by changing the syntax of the property to `.observes('*myProperty.[]')`. To support `Ember.Array` in your own class, you must override two @@ -9255,6 +8617,9 @@ var get = Ember.get, set = Ember.set, isNone = Ember.isNone, map = Ember.Enumera */ Ember.Array = Ember.Mixin.create(Ember.Enumerable, /** @scope Ember.Array.prototype */ { + // compatibility + isSCArray: true, + /** Your array must support the `length` property. Your replace methods should set this property whenever it changes. @@ -9294,7 +8659,7 @@ Ember.Array = Ember.Mixin.create(Ember.Enumerable, /** @scope Ember.Array.protot This returns the objects at the specified indexes, using `objectAt`. ```javascript - var arr = ['a', 'b', 'c', 'd']; + var arr = ['a', 'b', 'c', 'd']; arr.objectsAt([0, 1, 2]); // ["a", "b", "c"] arr.objectsAt([2, 3, 4]); // ["c", "d", undefined] ``` @@ -9362,8 +8727,8 @@ Ember.Array = Ember.Mixin.create(Ember.Enumerable, /** @scope Ember.Array.protot slice: function(beginIndex, endIndex) { var ret = Ember.A([]); var length = get(this, 'length') ; - if (isNone(beginIndex)) beginIndex = 0 ; - if (isNone(endIndex) || (endIndex > length)) endIndex = length ; + if (none(beginIndex)) beginIndex = 0 ; + if (none(endIndex) || (endIndex > length)) endIndex = length ; if (beginIndex < 0) beginIndex = length + beginIndex; if (endIndex < 0) endIndex = length + endIndex; @@ -9523,7 +8888,7 @@ Ember.Array = Ember.Mixin.create(Ember.Enumerable, /** @scope Ember.Array.protot @param {Number} startIdx The starting index in the array that will change. @param {Number} removeAmt The number of items that will be removed. If you pass `null` assumes 0 - @param {Number} addAmt The number of items that will be added. If you + @param {Number} addAmt The number of items that will be added If you pass `null` assumes 0. @return {Ember.Array} receiver */ @@ -9557,20 +8922,6 @@ Ember.Array = Ember.Mixin.create(Ember.Enumerable, /** @scope Ember.Array.protot return this; }, - /** - If you are implementing an object that supports `Ember.Array`, call this - method just after the array content changes to notify any observers and - invalidate any related properties. Pass the starting index of the change - as well as a delta of the amounts to change. - - @method arrayContentDidChange - @param {Number} startIdx The starting index in the array that did change. - @param {Number} removeAmt The number of items that were removed. If you - pass `null` assumes 0 - @param {Number} addAmt The number of items that were added. If you - pass `null` assumes 0. - @return {Ember.Array} receiver - */ arrayContentDidChange: function(startIdx, removeAmt, addAmt) { // if no args are passed assume everything changes @@ -9711,7 +9062,8 @@ var get = Ember.get, set = Ember.set; @extends Ember.Mixin @since Ember 0.9 */ -Ember.Copyable = Ember.Mixin.create(/** @scope Ember.Copyable.prototype */ { +Ember.Copyable = Ember.Mixin.create( +/** @scope Ember.Copyable.prototype */ { /** Override to return a copy of the receiver. Default implementation raises @@ -9816,7 +9168,8 @@ var get = Ember.get, set = Ember.set; @extends Ember.Mixin @since Ember 0.9 */ -Ember.Freezable = Ember.Mixin.create(/** @scope Ember.Freezable.prototype */ { +Ember.Freezable = Ember.Mixin.create( +/** @scope Ember.Freezable.prototype */ { /** Set to `true` when the object is frozen. Use this property to detect @@ -9997,7 +9350,8 @@ var get = Ember.get, set = Ember.set; @uses Ember.Array @uses Ember.MutableEnumerable */ -Ember.MutableArray = Ember.Mixin.create(Ember.Array, Ember.MutableEnumerable,/** @scope Ember.MutableArray.prototype */ { +Ember.MutableArray = Ember.Mixin.create(Ember.Array, Ember.MutableEnumerable, + /** @scope Ember.MutableArray.prototype */ { /** __Required.__ You must implement this method to apply this mixin. @@ -10268,6 +9622,7 @@ Ember.MutableArray = Ember.Mixin.create(Ember.Array, Ember.MutableEnumerable,/** }); + })(); @@ -10315,7 +9670,7 @@ var get = Ember.get, set = Ember.set; For example: ```javascript - Ember.Object.extend({ + Ember.Object.create({ valueObserver: function() { // Executes whenever the "value" property changes }.observes('value') @@ -10334,8 +9689,8 @@ var get = Ember.get, set = Ember.set; object.addObserver('propertyKey', targetObject, targetAction) ``` - This will call the `targetAction` method on the `targetObject` whenever - the value of the `propertyKey` changes. + This will call the `targetAction` method on the `targetObject` to be called + whenever the value of the `propertyKey` changes. Note that if `propertyKey` is a computed property, the observer will be called when any of the property dependencies are changed, even if the @@ -10595,8 +9950,8 @@ Ember.Observable = Ember.Mixin.create(/** @scope Ember.Observable.prototype */ { This is the core method used to register an observer for a property. - Once you call this method, any time the key's value is set, your observer - will be notified. Note that the observers are triggered any time the + Once you call this method, anytime the key's value is set, your observer + will be notified. Note that the observers are triggered anytime the value is set, regardless of whether it has actually changed. Your observer should be prepared to handle that. @@ -10721,11 +10076,11 @@ Ember.Observable = Ember.Mixin.create(/** @scope Ember.Observable.prototype */ { @method incrementProperty @param {String} keyName The name of the property to increment - @param {Number} increment The amount to increment by. Defaults to 1 - @return {Number} The new property value + @param {Object} increment The amount to increment by. Defaults to 1 + @return {Object} The new property value */ incrementProperty: function(keyName, increment) { - if (Ember.isNone(increment)) { increment = 1; } + if (!increment) { increment = 1; } set(this, keyName, (get(this, keyName) || 0)+increment); return get(this, keyName); }, @@ -10740,12 +10095,12 @@ Ember.Observable = Ember.Mixin.create(/** @scope Ember.Observable.prototype */ { @method decrementProperty @param {String} keyName The name of the property to decrement - @param {Number} decrement The amount to decrement by. Defaults to 1 - @return {Number} The new property value + @param {Object} increment The amount to decrement by. Defaults to 1 + @return {Object} The new property value */ - decrementProperty: function(keyName, decrement) { - if (Ember.isNone(decrement)) { decrement = 1; } - set(this, keyName, (get(this, keyName) || 0)-decrement); + decrementProperty: function(keyName, increment) { + if (!increment) { increment = 1; } + set(this, keyName, (get(this, keyName) || 0)-increment); return get(this, keyName); }, @@ -10754,7 +10109,7 @@ Ember.Observable = Ember.Mixin.create(/** @scope Ember.Observable.prototype */ { current value. ```javascript - starship.toggleProperty('warpDriveEngaged'); + starship.toggleProperty('warpDriveEnaged'); ``` @method toggleProperty @@ -10786,6 +10141,7 @@ Ember.Observable = Ember.Mixin.create(/** @scope Ember.Observable.prototype */ { } }); + })(); @@ -11080,9 +10436,9 @@ Ember.Evented = Ember.Mixin.create({ (function() { var RSVP = requireModule("rsvp"); -RSVP.configure('async', function(callback, binding) { +RSVP.async = function(callback, binding) { Ember.run.schedule('actions', binding, callback); -}); +}; /** @module ember @@ -11104,22 +10460,9 @@ Ember.DeferredMixin = Ember.Mixin.create({ @param {Function} doneCallback a callback function to be called when done @param {Function} failCallback a callback function to be called when failed */ - then: function(resolve, reject) { - var deferred, promise, entity; - - entity = this; - deferred = get(this, '_deferred'); - promise = deferred.promise; - - function fulfillmentHandler(fulfillment) { - if (fulfillment === promise) { - return resolve(entity); - } else { - return resolve(fulfillment); - } - } - - return promise.then(resolve && fulfillmentHandler, reject); + then: function(doneCallback, failCallback) { + var promise = get(this, 'promise'); + return promise.then.apply(promise, arguments); }, /** @@ -11128,16 +10471,7 @@ Ember.DeferredMixin = Ember.Mixin.create({ @method resolve */ resolve: function(value) { - var deferred, promise; - - deferred = get(this, '_deferred'); - promise = deferred.promise; - - if (value === this){ - deferred.resolve(promise); - } else { - deferred.resolve(value); - } + get(this, 'promise').resolve(value); }, /** @@ -11146,11 +10480,11 @@ Ember.DeferredMixin = Ember.Mixin.create({ @method reject */ reject: function(value) { - get(this, '_deferred').reject(value); + get(this, 'promise').reject(value); }, - _deferred: Ember.computed(function() { - return RSVP.defer(); + promise: Ember.computed(function() { + return new RSVP.Promise(); }) }); @@ -11463,23 +10797,21 @@ CoreObject.PrototypeMixin = Mixin.create({ raised. Note that destruction is scheduled for the end of the run loop and does not - happen immediately. It will set an isDestroying flag immediately. + happen immediately. @method destroy @return {Ember.Object} receiver */ destroy: function() { - if (this.isDestroying) { return; } - this.isDestroying = true; + if (this._didCallDestroy) { return; } + + this.isDestroying = true; + this._didCallDestroy = true; - schedule('actions', this, this.willDestroy); schedule('destroy', this, this._scheduledDestroy); return this; }, - /** - Override to implement teardown. - */ willDestroy: Ember.K, /** @@ -11491,9 +10823,10 @@ CoreObject.PrototypeMixin = Mixin.create({ @method _scheduledDestroy */ _scheduledDestroy: function() { - if (this.isDestroyed) { return; } + if (this.willDestroy) { this.willDestroy(); } destroy(this); this.isDestroyed = true; + if (this.didDestroy) { this.didDestroy(); } }, bind: function(to, from) { @@ -11984,7 +11317,8 @@ var get = Ember.get, set = Ember.set; @extends Ember.Object @uses Ember.MutableArray */ -Ember.ArrayProxy = Ember.Object.extend(Ember.MutableArray,/** @scope Ember.ArrayProxy.prototype */ { +Ember.ArrayProxy = Ember.Object.extend(Ember.MutableArray, +/** @scope Ember.ArrayProxy.prototype */ { /** The content array. Must be an object that implements `Ember.Array` and/or @@ -12260,6 +11594,7 @@ Ember.ArrayProxy = Ember.Object.extend(Ember.MutableArray,/** @scope Ember.Array } }); + })(); @@ -12359,7 +11694,8 @@ function contentPropertyDidChange(content, contentKey) { @namespace Ember @extends Ember.Object */ -Ember.ObjectProxy = Ember.Object.extend(/** @scope Ember.ObjectProxy.prototype */ { +Ember.ObjectProxy = Ember.Object.extend( +/** @scope Ember.ObjectProxy.prototype */ { /** The object whose properties will be forwarded. @@ -12797,7 +12133,7 @@ if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.Array) { @submodule ember-runtime */ -var get = Ember.get, set = Ember.set, guidFor = Ember.guidFor, isNone = Ember.isNone, fmt = Ember.String.fmt; +var get = Ember.get, set = Ember.set, guidFor = Ember.guidFor, none = Ember.isNone, fmt = Ember.String.fmt; /** An unordered collection of objects. @@ -13155,7 +12491,7 @@ Ember.Set = Ember.CoreObject.extend(Ember.MutableEnumerable, Ember.Copyable, Emb // implements Ember.MutableEnumerable addObject: function(obj) { if (get(this, 'isFrozen')) throw new Error(Ember.FROZEN_ERROR); - if (isNone(obj)) return this; // nothing to do + if (none(obj)) return this; // nothing to do var guid = guidFor(obj), idx = this[guid], @@ -13183,7 +12519,7 @@ Ember.Set = Ember.CoreObject.extend(Ember.MutableEnumerable, Ember.Copyable, Emb // implements Ember.MutableEnumerable removeObject: function(obj) { if (get(this, 'isFrozen')) throw new Error(Ember.FROZEN_ERROR); - if (isNone(obj)) return this; // nothing to do + if (none(obj)) return this; // nothing to do var guid = guidFor(obj), idx = this[guid], @@ -13258,7 +12594,7 @@ Deferred.reopenClass({ promise: function(callback, binding) { var deferred = Deferred.create(); callback.call(binding, deferred); - return deferred; + return get(deferred, 'promise'); } }); @@ -13269,8 +12605,6 @@ Ember.Deferred = Deferred; (function() { -var forEach = Ember.ArrayPolyfills.forEach; - /** @module ember @submodule ember-runtime @@ -13303,10 +12637,12 @@ Ember.onLoad = function(name, callback) { @param object {Object} object to pass to callbacks */ Ember.runLoadHooks = function(name, object) { + var hooks; + loaded[name] = object; - if (loadHooks[name]) { - forEach.call(loadHooks[name], function(callback) { + if (hooks = loadHooks[name]) { + loadHooks[name].forEach(function(callback) { callback(object); }); } @@ -13380,8 +12716,6 @@ Ember.ControllerMixin = Ember.Mixin.create({ container: null, - parentController: null, - store: null, model: Ember.computed.alias('content'), @@ -13724,10 +13058,6 @@ var get = Ember.get, set = Ember.set, forEach = Ember.EnumerableUtils.forEach, }); ``` - The itemController instances will have a `parentController` property set to - either the the `parentController` property of the `ArrayController` - or to the `ArrayController` instance itself. - @class ArrayController @namespace Ember @extends Ember.ArrayProxy @@ -13838,7 +13168,6 @@ Ember.ArrayController = Ember.ArrayProxy.extend(Ember.ControllerMixin, } subController.set('target', this); - subController.set('parentController', get(this, 'parentController') || this); subController.set('content', object); return subController; @@ -13912,7 +13241,7 @@ Ember Runtime */ var jQuery = Ember.imports.jQuery; -Ember.assert("Ember Views require jQuery 1.8, 1.9, 1.10, or 2.0", jQuery && (jQuery().jquery.match(/^((1\.(8|9|10))|2.0)(\.\d+)?(pre|rc\d?)?/) || Ember.ENV.FORCE_JQUERY)); +Ember.assert("Ember Views require jQuery 1.8, 1.9 or 2.0", jQuery && (jQuery().jquery.match(/^((1\.(8|9))|2.0)(\.\d+)?(pre|rc\d?)?/) || Ember.ENV.FORCE_JQUERY)); /** Alias for jQuery @@ -13952,7 +13281,7 @@ if (Ember.$) { @submodule ember-views */ -/* BEGIN METAMORPH HELPERS */ +/*** BEGIN METAMORPH HELPERS ***/ // Internet Explorer prior to 9 does not allow setting innerHTML if the first element // is a "zero-scope" element. This problem can be worked around by making @@ -14025,7 +13354,7 @@ var setInnerHTMLWithoutFix = function(element, html) { } }; -/* END METAMORPH HELPERS */ +/*** END METAMORPH HELPERS */ var innerHTMLTags = {}; @@ -14608,7 +13937,8 @@ var get = Ember.get, set = Ember.set, fmt = Ember.String.fmt; @private @extends Ember.Object */ -Ember.EventDispatcher = Ember.Object.extend(/** @scope Ember.EventDispatcher.prototype */{ +Ember.EventDispatcher = Ember.Object.extend( +/** @scope Ember.EventDispatcher.prototype */{ /** @private @@ -14640,10 +13970,10 @@ Ember.EventDispatcher = Ember.Object.extend(/** @scope Ember.EventDispatcher.pro @method setup @param addedEvents {Hash} */ - setup: function(addedEvents, rootElement) { + setup: function(addedEvents) { var event, events = { touchstart : 'touchStart', - touchmove : 'touchMove', + // touchmove : 'touchMove', touchend : 'touchEnd', touchcancel : 'touchCancel', keydown : 'keyDown', @@ -14654,7 +13984,7 @@ Ember.EventDispatcher = Ember.Object.extend(/** @scope Ember.EventDispatcher.pro contextmenu : 'contextMenu', click : 'click', dblclick : 'doubleClick', - mousemove : 'mouseMove', + // mousemove : 'mouseMove', focusin : 'focusIn', focusout : 'focusOut', mouseenter : 'mouseEnter', @@ -14673,12 +14003,7 @@ Ember.EventDispatcher = Ember.Object.extend(/** @scope Ember.EventDispatcher.pro Ember.$.extend(events, addedEvents || {}); - - if (!Ember.isNone(rootElement)) { - set(this, 'rootElement', rootElement); - } - - rootElement = Ember.$(get(this, 'rootElement')); + var rootElement = Ember.$(get(this, 'rootElement')); Ember.assert(fmt('You cannot use the same root element (%@) multiple times in an Ember.Application', [rootElement.selector || rootElement[0].tagName]), !rootElement.is('.ember-application')); Ember.assert('You cannot make a new Ember.Application using a root element that is a descendent of an existing Ember.Application', !rootElement.closest('.ember-application').length); @@ -14720,7 +14045,7 @@ Ember.EventDispatcher = Ember.Object.extend(/** @scope Ember.EventDispatcher.pro setupHandler: function(rootElement, event, eventName) { var self = this; - rootElement.on(event + '.ember', '.ember-view', function(evt, triggeringManager) { + rootElement.delegate('.ember-view', event + '.ember', function(evt, triggeringManager) { return Ember.handleErrors(function() { var view = Ember.View.views[this.id], result = true, manager = null; @@ -14739,7 +14064,7 @@ Ember.EventDispatcher = Ember.Object.extend(/** @scope Ember.EventDispatcher.pro }, this); }); - rootElement.on(event + '.ember', '[data-ember-action]', function(evt) { + rootElement.delegate('[data-ember-action]', event + '.ember', function(evt) { return Ember.handleErrors(function() { var actionId = Ember.$(evt.currentTarget).attr('data-ember-action'), action = Ember.Handlebars.ActionHelper.registeredActions[actionId]; @@ -14791,7 +14116,7 @@ Ember.EventDispatcher = Ember.Object.extend(/** @scope Ember.EventDispatcher.pro destroy: function() { var rootElement = get(this, 'rootElement'); - Ember.$(rootElement).off('.ember', '**').removeClass('ember-application'); + Ember.$(rootElement).undelegate('.ember').removeClass('ember-application'); return this._super(); } }); @@ -15594,7 +14919,7 @@ class: eventManager: Ember.Object.create({ mouseEnter: function(event, view){ // view might be instance of either - // OuterView or InnerView depending on + // OutsideView or InnerView depending on // where on the page the user interaction occured } }) @@ -15788,8 +15113,6 @@ Ember.View = Ember.CoreView.extend( templateForName: function(name, type) { if (!name) { return; } Ember.assert("templateNames are not allowed to contain periods: "+name, name.indexOf('.') === -1); - - // the defaultContainer is deprecated var container = this.container || (Ember.Container && Ember.Container.defaultContainer); return container && container.lookup('template:' + name); }, @@ -17260,7 +16583,7 @@ Ember.View.reopenClass({ // If the value is not false, undefined, or null, return the current // value of the property. - } else if (val !== false && val != null) { + } else if (val !== false && val !== undefined && val !== null) { return val; // Nothing to display. Return null so that the old class is removed @@ -17908,8 +17231,6 @@ Ember.ContainerView = Ember.View.extend(Ember.MutableArray, { replace: function(idx, removedCount, addedViews) { var addedCount = addedViews ? get(addedViews, 'length') : 0; - var self = this; - Ember.assert("You can't add a child to a container that is already a child of another view", Ember.A(addedViews).every(function(item) { return !get(item, '_parentView') || get(item, '_parentView') === self; })); this.arrayContentWillChange(idx, removedCount, addedCount); this.childViewsWillChange(this._childViews, idx, removedCount); @@ -17950,7 +17271,7 @@ Ember.ContainerView = Ember.View.extend(Ember.MutableArray, { }); }, - instrumentName: 'container', + instrumentName: 'render.container', /** @private @@ -18030,7 +17351,6 @@ Ember.ContainerView = Ember.View.extend(Ember.MutableArray, { _currentViewDidChange: Ember.observer(function() { var currentView = get(this, 'currentView'); if (currentView) { - Ember.assert("You tried to set a current view that already has a parent. Make sure you don't have multiple outlets in the same view.", !get(currentView, '_parentView')); this.pushObject(currentView); } }, 'currentView'), @@ -18120,7 +17440,7 @@ var get = Ember.get, set = Ember.set, fmt = Ember.String.fmt; /** `Ember.CollectionView` is an `Ember.View` descendent responsible for managing - a collection (an array or array-like object) by maintaining a child view object + a collection (an array or array-like object) by maintaing a child view object and associated DOM representation for each item in the array and ensuring that child views and their associated rendered HTML are updated when items in the array are added, removed, or replaced. @@ -18271,7 +17591,8 @@ var get = Ember.get, set = Ember.set, fmt = Ember.String.fmt; @extends Ember.ContainerView @since Ember 0.9 */ -Ember.CollectionView = Ember.ContainerView.extend(/** @scope Ember.CollectionView.prototype */ { +Ember.CollectionView = Ember.ContainerView.extend( +/** @scope Ember.CollectionView.prototype */ { /** A list of items to be displayed by the `Ember.CollectionView`. @@ -18397,10 +17718,9 @@ Ember.CollectionView = Ember.ContainerView.extend(/** @scope Ember.CollectionVie This array observer is added in `contentDidChange`. @method arrayDidChange - @param {Array} content the managed collection of objects - @param {Number} start the index at which the changes occurred - @param {Number} removed number of object removed from content - @param {Number} added number of object added to content + @param {Array} addedObjects the objects that were added to the content + @param {Array} removedObjects the objects that were removed from the content + @param {Number} changeIndex the index at which the changes occurred */ arrayDidChange: function(content, start, removed, added) { var itemViewClass = get(this, 'itemViewClass'), @@ -19038,8 +18358,7 @@ if(!Handlebars && typeof require === 'function') { Handlebars = require('handlebars'); } -Ember.assert("Ember Handlebars requires Handlebars version 1.0.0-rc.4. Include a SCRIPT tag in the HTML HEAD linking to the Handlebars file before you link to Ember.", Handlebars) -Ember.assert("Ember Handlebars requires Handlebars version 1.0.0-rc.4, COMPILER_REVISION expected: 3, got: " + Handlebars.COMPILER_REVISION + " – Please note: Builds of master may have other COMPILER_REVISION values.", Handlebars.COMPILER_REVISION === 3); +Ember.assert("Ember Handlebars requires Handlebars 1.0.0-rc.3 or greater. Include a SCRIPT tag in the HTML HEAD linking to the Handlebars file before you link to Ember.", Handlebars && Handlebars.COMPILER_REVISION === 2); /** Prepares the Handlebars templating library for use inside Ember's view @@ -19057,25 +18376,10 @@ Ember.assert("Ember Handlebars requires Handlebars version 1.0.0-rc.4, COMPILER_ */ Ember.Handlebars = objectCreate(Handlebars); -function makeBindings(options) { - var hash = options.hash, - hashType = options.hashTypes; - - for (var prop in hash) { - if (hashType[prop] === 'ID') { - hash[prop + 'Binding'] = hash[prop]; - hashType[prop + 'Binding'] = 'STRING'; - delete hash[prop]; - delete hashType[prop]; - } - } -} - Ember.Handlebars.helper = function(name, value) { if (Ember.View.detect(value)) { - Ember.Handlebars.registerHelper(name, function(options) { - Ember.assert("You can only pass attributes as parameters (not values) to a application-defined helper", arguments.length < 2); - makeBindings(options); + Ember.Handlebars.registerHelper(name, function(name, options) { + Ember.assert("You can only pass attributes as parameters to a application-defined helper", arguments.length < 3); return Ember.Handlebars.helpers.view.call(this, value, options); }); } else { @@ -19451,15 +18755,15 @@ Ember.Handlebars.registerHelper('helperMissing', function(path, options) { }); ``` - Which allows for template syntax such as `{{concatenate prop1 prop2}}` or - `{{concatenate prop1 prop2 prop3}}`. If any of the properties change, + Which allows for template syntax such as {{concatenate prop1 prop2}} or + {{concatenate prop1 prop2 prop3}}. If any of the properties change, the helpr will re-render. Note that dependency keys cannot be using in conjunction with multi-property helpers, since it is ambiguous which property the dependent keys would belong to. ## Use with unbound helper - The `{{unbound}}` helper can be used with bound helper invocations + The {{unbound}} helper can be used with bound helper invocations to render them in their unbound form, e.g. ```handlebars @@ -19469,10 +18773,6 @@ Ember.Handlebars.registerHelper('helperMissing', function(path, options) { In this example, if the name property changes, the helper will not re-render. - ## Use with blocks not supported - - Bound helpers do not support use with Handlebars blocks or - the addition of child views of any kind. @method registerBoundHelper @for Ember.Handlebars @@ -19496,8 +18796,6 @@ Ember.Handlebars.registerBoundHelper = function(name, fn) { pathRoot, path, loc, hashOption; - Ember.assert("registerBoundHelper-generated helpers do not support use with Handlebars blocks.", !options.fn); - // Detect bound options (e.g. countBinding="otherCount") hash.boundOptions = {}; for (hashOption in hash) { @@ -19658,6 +18956,7 @@ Ember.Handlebars.template = function(spec){ return t; }; + })(); @@ -19677,7 +18976,7 @@ var htmlSafe = Ember.String.htmlSafe; if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.String) { /** - See `Ember.String.htmlSafe`. + See {{#crossLink "Ember.String/htmlSafe"}}{{/crossLink}} @method htmlSafe @for String @@ -19793,7 +19092,7 @@ Ember._Metamorph = Ember.Mixin.create({ isVirtual: true, tagName: '', - instrumentName: 'metamorph', + instrumentName: 'render.metamorph', init: function() { this._super(); @@ -19982,7 +19281,7 @@ merge(states.inDOM, { @private */ Ember._HandlebarsBoundView = Ember._MetamorphView.extend({ - instrumentName: 'boundHandlebars', + instrumentName: 'render.boundHandlebars', states: states, /** @@ -20619,7 +19918,7 @@ EmberHandlebars.registerHelper('bindAttr', function(options) { // Handle classes differently, as we can bind multiple classes var classBindings = attrs['class']; - if (classBindings != null) { + if (classBindings !== null && classBindings !== undefined) { var classResults = EmberHandlebars.bindClasses(this, classBindings, view, dataId, options); ret.push('class="' + Handlebars.Utils.escapeExpression(classResults.join(' ')) + '"'); @@ -21471,7 +20770,6 @@ Ember.Handlebars.EachView = Ember.CollectionView.extend(Ember._Metamorph, { set(controller, 'container', get(this, 'controller.container')); set(controller, '_eachView', this); set(controller, 'target', get(this, 'controller')); - set(controller, 'parentController', get(this, 'controller')); this.disableContentObservers(function() { set(this, 'content', controller); @@ -21745,9 +21043,6 @@ GroupedEach.prototype = { {{/each}} ``` - Each itemController will receive a reference to the current controller as - a `parentController` property. - @method each @for Ember.Handlebars.helpers @param [name] {String} name for item (used with `in`) @@ -21769,11 +21064,6 @@ Ember.Handlebars.registerHelper('each', function(path, options) { options.hash.keyword = keywordName; } - if (arguments.length === 1) { - options = path; - path = 'this'; - } - options.hash.dataSourceBinding = path; // Set up emptyView as a metamorph with no tag //options.hash.emptyViewClass = Ember._MetamorphView; @@ -21892,7 +21182,7 @@ Ember.Handlebars.registerHelper('partial', function(name, options) { var view = options.data.view, underscoredName = nameParts.join("/"), template = view.templateForName(underscoredName), - deprecatedTemplate = !template && view.templateForName(name); + deprecatedTemplate = view.templateForName(name); Ember.deprecate("You tried to render the partial " + name + ", which should be at '" + underscoredName + "', but Ember found '" + name + "'. Please use a leading underscore in your partials", template); Ember.assert("Unable to find partial with name '"+name+"'.", template || deprecatedTemplate); @@ -22018,7 +21308,7 @@ var set = Ember.set, get = Ember.get; You can add a `label` tag yourself in the template where the `Ember.Checkbox` is being used. - ```handlebars + ```html