Skip to content

Commit

Permalink
Merge branch 'master' into simplify-properties
Browse files Browse the repository at this point in the history
Conflicts:
	packages/ember-runtime/lib/system/object_proxy.js
  • Loading branch information
krisselden committed Jul 6, 2012
2 parents 73f3638 + 3fb87cf commit b57ed3e
Show file tree
Hide file tree
Showing 24 changed files with 306 additions and 69 deletions.
2 changes: 1 addition & 1 deletion Assetfile
Expand Up @@ -85,7 +85,7 @@ end

distros = {
:runtime => %w(ember-metal ember-runtime),
:full => %w(handlebars ember-metal ember-runtime ember-application ember-views ember-states ember-routing ember-viewstates metamorph ember-handlebars)
:full => %w(ember-metal ember-runtime ember-application ember-views ember-states ember-routing ember-viewstates metamorph ember-handlebars)
}

output "dist"
Expand Down
4 changes: 2 additions & 2 deletions Rakefile
Expand Up @@ -67,12 +67,12 @@ end
desc "Clean build artifacts from previous builds"
task :clean do
puts "Cleaning build..."
pipeline.clean
rm_rf "dist" # Make sure even things RakeP doesn't know about are cleaned
puts "Done"
end

desc "Upload latest Ember.js build to GitHub repository"
task :upload_latest => :dist do
task :upload_latest => [:clean, :dist] do
uploader = setup_uploader

# Upload minified first, so non-minified shows up on top
Expand Down
7 changes: 7 additions & 0 deletions benchmarks/README.md
@@ -0,0 +1,7 @@
# Extremely simple Ember benchmarks

To run the benchmarks, serve the repository root on a web server (`gem install
asdf; asdf`), run `rake` to build Ember, and open e.g.
`http://localhost:9292/benchmarks/index.html?suitePath=plain_object.js` to run
`benchmarks/suites/plain_object.js`. Run `cp -r dist distold` to benchmark
different versions against each other.
1 change: 0 additions & 1 deletion benchmarks/iframe_runner.js
Expand Up @@ -10,7 +10,6 @@ BenchWarmer.evalString = function(string, emberPath, logger, profile) {
var benchWarmer = new BenchWarmer(emberPath, logger, profile);

var bench = function(name, fn) {
ember_assert("Please pass in a name and function", arguments.length === 2);
benchWarmer.bench(name, fn);
};

Expand Down
19 changes: 14 additions & 5 deletions packages/ember-application/lib/system/history_location.js
Expand Up @@ -7,9 +7,17 @@ var get = Ember.get, set = Ember.set;
Ember.HistoryLocation = Ember.Object.extend({
init: function() {
set(this, 'location', get(this, 'location') || window.location);
set(this, '_initialURL', get(this, 'location').pathname);
set(this, 'callbacks', Ember.A());
},

/**
@private
Used to give history a starting reference
*/
_initialURL: null,

/**
@private
Expand All @@ -25,12 +33,13 @@ Ember.HistoryLocation = Ember.Object.extend({
Uses `history.pushState` to update the url without a page reload.
*/
setURL: function(path) {
var state = window.history.state;
var state = window.history.state,
initialURL = get(this, '_initialURL');

if (path === "") { path = '/'; }
// We only want pushState to be executed if we are passing
// in a new path, otherwise a new state will be inserted
// for the same path.
if (!state || (state && state.path !== path)) {

if ((initialURL && initialURL !== path) || (state && state.path !== path)) {
set(this, '_initialURL', null);
window.history.pushState({ path: path }, null, path);
}
},
Expand Down
21 changes: 20 additions & 1 deletion packages/ember-application/tests/system/location_test.js
@@ -1,5 +1,5 @@
var locationObject;
var realPushState;
var realPushState, realHistoryState;

module("Ember.Location, hash implementation", {
setup: function() {
Expand Down Expand Up @@ -64,6 +64,7 @@ test("if the URL is set, it doesn't trigger the hashchange event", function() {

module("Ember.Location, history implementation", {
setup: function() {
realHistoryState = window.history.state;
realPushState = window.history.pushState;
locationObject = Ember.Location.create({
implementation: 'history'
Expand All @@ -75,6 +76,7 @@ module("Ember.Location, history implementation", {

teardown: function() {
window.history.pushState = realPushState;
window.history.state = realHistoryState;
Ember.run(function() {
locationObject.destroy();
});
Expand Down Expand Up @@ -130,3 +132,20 @@ test("if history is used, it triggers the popstate event", function() {

window.history.back();
});

test("doesn't push a state if path has not changed", function() {
expect(1);
stop();

var count = 0;
window.history.pushState = function(data, title, path) {
count++;
};

setTimeout(function() {
start();
equal(count, 0, "pushState should not have been called");
}, 100);

locationObject.setURL(window.location.pathname);
});
1 change: 1 addition & 0 deletions packages/ember-handlebars/lib/helpers/action.js
Expand Up @@ -19,6 +19,7 @@ ActionHelper.registerAction = function(actionName, eventName, target, view, cont
if (target.isState && typeof target.send === 'function') {
return target.send(actionName, event);
} else {
Ember.assert(Ember.String.fmt('Target %@ does not have action %@', [target, actionName]), target[actionName]);
return target[actionName].call(target, event);
}
}
Expand Down
2 changes: 1 addition & 1 deletion packages/ember-handlebars/lib/helpers/binding.js
Expand Up @@ -167,7 +167,7 @@ EmberHandlebars.registerHelper('with', function(context, options) {

Ember.assert("You must pass a block to the with helper", options.fn && options.fn !== Handlebars.VM.noop);

if (Ember.isGlobal(path)) {
if (Ember.isGlobalPath(path)) {
Ember.bind(options.data.keywords, keywordName, path);
} else {
normalized = normalizePath(this, path, options.data);
Expand Down
7 changes: 1 addition & 6 deletions packages/ember-handlebars/lib/loader.js
Expand Up @@ -36,7 +36,7 @@ Ember.Handlebars.bootstrap = function(ctx) {
// id if no name is found.
templateName = script.attr('data-template-name') || script.attr('id'),
template = compile(script.html()),
view, viewPath, elementId, tagName, options;
view, viewPath, elementId, options;

if (templateName) {
// For templates which have a name, we save them and then remove them from the DOM
Expand Down Expand Up @@ -65,13 +65,8 @@ Ember.Handlebars.bootstrap = function(ctx) {
// Look for data-element-id attribute.
elementId = script.attr('data-element-id');

// Users can optionally specify a custom tag name to use by setting the
// data-tag-name attribute on the script tag.
tagName = script.attr('data-tag-name');

options = { template: template };
if (elementId) { options.elementId = elementId; }
if (tagName) { options.tagName = tagName; }

view = view.create(options);

Expand Down
29 changes: 29 additions & 0 deletions packages/ember-handlebars/tests/helpers/each_test.js
Expand Up @@ -191,4 +191,33 @@ if (Ember.VIEW_PRESERVES_CONTEXT) {
equal(view.$().text(), "My Cool Each Test 1My Cool Each Test 2");
});

test("views inside #each preserve the new context", function() {
var controller = Ember.A([ { name: "Adam" }, { name: "Steve" } ]);

view = Ember.View.create({
controller: controller,
template: templateFor('{{#each controller}}{{#view}}{{name}}{{/view}}{{/each}}')
});

append(view);

equal(view.$().text(), "AdamSteve");
});

test("views inside #each use a controller as their context if set explicitly", function() {
var controller = Ember.A([ { name: "Adam" }, { name: "Steve" } ]);

view = Ember.View.create({
controller: controller,
template: templateFor('{{#each controller}}{{#view view.itemView}}{{name}}{{/view}}{{/each}}'),

itemView: Ember.View.extend({
controller: { name: "Ryan" }
})
});

append(view);

equal(view.$().text(), "RyanRyan");
});
}
14 changes: 0 additions & 14 deletions packages/ember-handlebars/tests/loader_test.js
Expand Up @@ -51,20 +51,6 @@ test('inline template should be added', function() {
equal(Ember.$('#qunit-fixture').text(), 'Tobias Fünke', 'template is rendered');
});

test('template with data-tag-name should add a template, wrapped in specific tag', function() {
Ember.$('#qunit-fixture').html('<script type="text/x-handlebars" data-tag-name="h1" >{{Tobias.firstName}} takes {{Tobias.drug}}</script>');

Ember.run(function() {
Ember.Handlebars.bootstrap(Ember.$('#qunit-fixture'));
Tobias = Ember.Object.create({
firstName: 'Tobias',
drug: 'teamocil'
});
});

equal(Ember.$('#qunit-fixture h1').text(), 'Tobias takes teamocil', 'template is rendered inside custom tag');
});

test('template with data-element-id should add an id attribute to the view', function() {
Ember.$('#qunit-fixture').html('<script type="text/x-handlebars" data-element-id="application">Hello World !</script>');

Expand Down
16 changes: 14 additions & 2 deletions packages/ember-handlebars/tests/views/collection_view_test.js
Expand Up @@ -172,7 +172,12 @@ test("a block passed to a collection helper defaults to the content property of
view.appendTo('#qunit-fixture');
});

equal(view.$('li:has(label:contains("foo")) + li:has(label:contains("bar")) + li:has(label:contains("baz"))').length, 1, 'one label element is created for each content item');
equal(view.$('li:nth-child(1) label').length, 1);
equal(view.$('li:nth-child(1) label').text(), 'foo');
equal(view.$('li:nth-child(2) label').length, 1);
equal(view.$('li:nth-child(2) label').text(), 'bar');
equal(view.$('li:nth-child(3) label').length, 1);
equal(view.$('li:nth-child(3) label').text(), 'baz');
});

test("a block passed to a collection helper defaults to the view", function() {
Expand All @@ -188,7 +193,14 @@ test("a block passed to a collection helper defaults to the view", function() {
Ember.run(function() {
view.appendTo('#qunit-fixture');
});
equal(view.$('li:has(label:contains("foo")) + li:has(label:contains("bar")) + li:has(label:contains("baz"))').length, 1, 'precond - one aside element is created for each content item');

// Preconds
equal(view.$('li:nth-child(1) label').length, 1);
equal(view.$('li:nth-child(1) label').text(), 'foo');
equal(view.$('li:nth-child(2) label').length, 1);
equal(view.$('li:nth-child(2) label').text(), 'bar');
equal(view.$('li:nth-child(3) label').length, 1);
equal(view.$('li:nth-child(3) label').text(), 'baz');

Ember.run(function() {
set(firstChild(view), 'content', Ember.A());
Expand Down
10 changes: 3 additions & 7 deletions packages/ember-metal/lib/accessors.js
Expand Up @@ -177,11 +177,6 @@ function normalizeTuple(target, path) {
return [ target, path ];
}

/** @private */
Ember.isGlobal = function(path) {
return IS_GLOBAL.test(path);
};

/**
@private
Expand Down Expand Up @@ -245,7 +240,8 @@ Ember.getPath = function(root, path) {
Ember.setPath = function(root, path, value, tolerant) {
var keyName;

if (typeof root === 'string' && IS_GLOBAL.test(root)) {
if (typeof root === 'string') {
Ember.assert("Path '" + root + "' must be global if no root is given.", IS_GLOBAL.test(root));
value = path;
path = root;
root = null;
Expand Down Expand Up @@ -299,5 +295,5 @@ Ember.trySetPath = function(root, path, value) {
@returns Boolean
*/
Ember.isGlobalPath = function(path) {
return !HAS_THIS.test(path) && IS_GLOBAL.test(path);
return IS_GLOBAL.test(path);
};
21 changes: 17 additions & 4 deletions packages/ember-routing/lib/routable.js
Expand Up @@ -78,7 +78,7 @@ Ember.Routable = Ember.Mixin.create({
In general, this will update the browser's URL.
*/
updateRoute: function(manager, location) {
if (get(this, 'isLeaf')) {
if (get(this, 'isLeafRoute')) {
var path = this.absoluteRoute(manager);
location.setURL(path);
}
Expand Down Expand Up @@ -130,6 +130,16 @@ Ember.Routable = Ember.Mixin.create({
return typeof get(this, 'route') === 'string';
}).cacheable(),

/**
@private
Determine if this is the last routeable state
*/
isLeafRoute: Ember.computed(function() {
if (get(this, 'isLeaf')) { return true; }
return !get(this, 'childStates').findProperty('isRoutable');
}).cacheable(),

/**
@private
Expand All @@ -146,9 +156,10 @@ Ember.Routable = Ember.Mixin.create({
/**
@private
Check whether the route has dynamic segments
Check whether the route has dynamic segments and therefore takes
a context.
*/
isDynamic: Ember.computed(function() {
hasContext: Ember.computed(function() {
var routeMatcher = get(this, 'routeMatcher');
if (routeMatcher) {
return routeMatcher.identifiers.length > 0;
Expand Down Expand Up @@ -272,10 +283,12 @@ Ember.Routable = Ember.Mixin.create({
on the state whose path is `/posts` with the path `/2/comments`.
*/
routePath: function(manager, path) {
if (get(this, 'isLeaf')) { return; }
if (get(this, 'isLeafRoute')) { return; }

var childStates = get(this, 'childStates'), match;

childStates = Ember.A(childStates.filterProperty('isRoutable'));

childStates = childStates.sort(function(a, b) {
var aDynamicSegments = getPath(a, 'routeMatcher.identifiers.length'),
bDynamicSegments = getPath(b, 'routeMatcher.identifiers.length'),
Expand Down
19 changes: 16 additions & 3 deletions packages/ember-routing/lib/router.js
Expand Up @@ -316,7 +316,7 @@ var get = Ember.get, getPath = Ember.getPath, set = Ember.set;
{{/each}}
</script>
See Handlebars.helpers.actions for additional usage examples.
See Handlebars.helpers.action for additional usage examples.
## Changing View Hierarchy in Response To State Change
Expand Down Expand Up @@ -389,21 +389,34 @@ Ember.Router = Ember.StateManager.extend(
route: function(path) {
set(this, 'isRouting', true);

var routableState;

try {
path = path.replace(/^(?=[^\/])/, "/");

this.send('navigateAway');
this.send('unroutePath', path);

var currentURL = get(this, 'currentState').absoluteRoute(this);
routableState = get(this, 'currentState');
while (routableState && !routableState.get('isRoutable')) {
routableState = get(routableState, 'parentState');
}
var currentURL = routableState ? routableState.absoluteRoute(this) : '';
var rest = path.substr(currentURL.length);

this.send('routePath', rest);
} finally {
set(this, 'isRouting', false);
}

get(this, 'currentState').updateRoute(this, get(this, 'location'));
routableState = get(this, 'currentState');
while (routableState && !routableState.get('isRoutable')) {
routableState = get(routableState, 'parentState');
}

if (routableState) {
routableState.updateRoute(this, get(this, 'location'));
}
},

urlFor: function(path, hash) {
Expand Down

0 comments on commit b57ed3e

Please sign in to comment.