Front-end Optimizations

This commit is contained in:
Robin Ward 2013-07-31 14:52:31 -04:00
parent 0480c55d9f
commit 51d86b7a91
4 changed files with 90 additions and 93 deletions

View File

@ -64,25 +64,32 @@ Discourse.HeaderView = Discourse.View.extend({
}, },
examineDockHeader: function() { examineDockHeader: function() {
var $body, offset, outlet;
if (!this.docAt) { var headerView = this;
outlet = $('#main-outlet');
if (!(outlet && outlet.length === 1)) return; // Check the dock after the current run loop. While rendering,
this.docAt = outlet.offset().top; // it's much slower to calculate `outlet.offset()`
} Em.run.next(function () {
offset = window.pageYOffset || $('html').scrollTop(); if (!headerView.docAt) {
if (offset >= this.docAt) { var outlet = $('#main-outlet');
if (!this.dockedHeader) { if (!(outlet && outlet.length === 1)) return;
$body = $('body'); headerView.docAt = outlet.offset().top;
$body.addClass('docked');
this.dockedHeader = true;
} }
} else {
if (this.dockedHeader) { var offset = window.pageYOffset || $('html').scrollTop();
$('body').removeClass('docked'); if (offset >= headerView.docAt) {
this.dockedHeader = false; if (!headerView.dockedHeader) {
$('body').addClass('docked');
headerView.dockedHeader = true;
}
} else {
if (headerView.dockedHeader) {
$('body').removeClass('docked');
headerView.dockedHeader = false;
}
} }
} });
}, },
/** /**

View File

@ -91,7 +91,11 @@ Discourse.TopicView = Discourse.View.extend(Discourse.Scrolling, {
this.bindScrolling({debounce: 0}); this.bindScrolling({debounce: 0});
var topicView = this; var topicView = this;
$(window).bind('resize.discourse-on-scroll', function() { topicView.updatePosition(); }); Em.run.schedule('afterRender', function () {
$(window).resize('resize.discourse-on-scroll', function() {
topicView.updatePosition();
});
});
this.$().on('mouseup.discourse-redirect', '.cooked a, a.track-link', function(e) { this.$().on('mouseup.discourse-redirect', '.cooked a, a.track-link', function(e) {
return Discourse.ClickTrack.trackClick(e); return Discourse.ClickTrack.trackClick(e);

View File

@ -1999,7 +1999,7 @@ function suspendListener(obj, eventName, target, method, callback) {
Suspends multiple listeners during a callback. Suspends multiple listeners during a callback.
@method suspendListeners @method suspendListeners
@for Ember @for Ember
@param obj @param obj
@ -2066,7 +2066,7 @@ function watchedEvents(obj) {
is skipped, and once listeners are removed. A listener without is skipped, and once listeners are removed. A listener without
a target is executed on the passed object. If an array of actions a target is executed on the passed object. If an array of actions
is not passed, the actions stored on the passed object are invoked. is not passed, the actions stored on the passed object are invoked.
@method sendEvent @method sendEvent
@for Ember @for Ember
@param obj @param obj
@ -2794,14 +2794,14 @@ Map.create = function() {
Map.prototype = { Map.prototype = {
/** /**
This property will change as the number of objects in the map changes. This property will change as the number of objects in the map changes.
@property length @property length
@type number @type number
@default 0 @default 0
*/ */
length: 0, length: 0,
/** /**
Retrieve the value associated with a given key. Retrieve the value associated with a given key.
@ -4404,7 +4404,7 @@ Ember.computed.alias = function(dependentKey) {
@return {Ember.ComputedProperty} computed property which creates an @return {Ember.ComputedProperty} computed property which creates an
one way computed property to the original value for property. one way computed property to the original value for property.
Where `computed.alias` aliases `get` and `set`, and allows for bidirectional Where `computed.alias` aliases `get` and `set`, and allows for bidirectional
data flow, `computed.oneWay` only provides an aliased `get`. The `set` will data flow, `computed.oneWay` only provides an aliased `get`. The `set` will
not mutate the upstream property, rather causes the current property to not mutate the upstream property, rather causes the current property to
become the value set. This causes the downstream property to permentantly become the value set. This causes the downstream property to permentantly
@ -15807,7 +15807,11 @@ ViewCollection.prototype = {
var views = this.views, view; var views = this.views, view;
for (var i = 0, l = views.length; i < l; i++) { for (var i = 0, l = views.length; i < l; i++) {
view = views[i]; view = views[i];
if (view.trigger) { view.trigger(eventName); }
if (view.trigger) {
t2 = new Date().getTime();
view.trigger(eventName);
}
} }
}, },
@ -17335,12 +17339,6 @@ Ember.View = Ember.CoreView.extend(
return viewCollection; return viewCollection;
}, },
_elementWillChange: Ember.beforeObserver(function() {
this.forEachChildView(function(view) {
Ember.propertyWillChange(view, 'element');
});
}, 'element'),
/** /**
@private @private
@ -17352,7 +17350,8 @@ Ember.View = Ember.CoreView.extend(
*/ */
_elementDidChange: Ember.observer(function() { _elementDidChange: Ember.observer(function() {
this.forEachChildView(function(view) { this.forEachChildView(function(view) {
Ember.propertyDidChange(view, 'element'); var meta = Em.meta(view);
delete meta.cache['element'];
}); });
}, 'element'), }, 'element'),
@ -20746,17 +20745,13 @@ var DOMManager = {
view.clearRenderedChildren(); view.clearRenderedChildren();
var buffer = view.renderToBuffer(); var buffer = view.renderToBuffer();
// view.invokeRecursively(function(view) { view.propertyWillChange('element');
// view.propertyWillChange('element');
// });
view.triggerRecursively('willInsertElement'); view.triggerRecursively('willInsertElement');
morph.replaceWith(buffer.string()); morph.replaceWith(buffer.string());
view.transitionTo('inDOM'); view.transitionTo('inDOM');
// view.invokeRecursively(function(view) { view.propertyDidChange('element');
// view.propertyDidChange('element');
// });
view.triggerRecursively('didInsertElement'); view.triggerRecursively('didInsertElement');
notifyMutationListeners(); notifyMutationListeners();
@ -23890,7 +23885,7 @@ helpers = this.merge(helpers, Ember.Handlebars.helpers); data = data || {};
var buffer = '', stack1, hashTypes, hashContexts, escapeExpression=this.escapeExpression, self=this; var buffer = '', stack1, hashTypes, hashContexts, escapeExpression=this.escapeExpression, self=this;
function program1(depth0,data) { function program1(depth0,data) {
var buffer = '', hashTypes, hashContexts; var buffer = '', hashTypes, hashContexts;
data.buffer.push("<option value=\"\">"); data.buffer.push("<option value=\"\">");
hashTypes = {}; hashTypes = {};
@ -23901,7 +23896,7 @@ function program1(depth0,data) {
} }
function program3(depth0,data) { function program3(depth0,data) {
var stack1, hashTypes, hashContexts; var stack1, hashTypes, hashContexts;
hashTypes = {}; hashTypes = {};
hashContexts = {}; hashContexts = {};
@ -23910,7 +23905,7 @@ function program3(depth0,data) {
else { data.buffer.push(''); } else { data.buffer.push(''); }
} }
function program4(depth0,data) { function program4(depth0,data) {
var hashContexts, hashTypes; var hashContexts, hashTypes;
hashContexts = {'contentBinding': depth0,'labelBinding': depth0}; hashContexts = {'contentBinding': depth0,'labelBinding': depth0};
hashTypes = {'contentBinding': "ID",'labelBinding': "ID"}; hashTypes = {'contentBinding': "ID",'labelBinding': "ID"};
@ -23921,7 +23916,7 @@ function program4(depth0,data) {
} }
function program6(depth0,data) { function program6(depth0,data) {
var stack1, hashTypes, hashContexts; var stack1, hashTypes, hashContexts;
hashTypes = {}; hashTypes = {};
hashContexts = {}; hashContexts = {};
@ -23930,7 +23925,7 @@ function program6(depth0,data) {
else { data.buffer.push(''); } else { data.buffer.push(''); }
} }
function program7(depth0,data) { function program7(depth0,data) {
var hashContexts, hashTypes; var hashContexts, hashTypes;
hashContexts = {'contentBinding': depth0}; hashContexts = {'contentBinding': depth0};
hashTypes = {'contentBinding': "STRING"}; hashTypes = {'contentBinding': "STRING"};
@ -23948,7 +23943,7 @@ function program7(depth0,data) {
stack1 = helpers['if'].call(depth0, "view.optionGroupPath", {hash:{},inverse:self.program(6, program6, data),fn:self.program(3, program3, data),contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data}); stack1 = helpers['if'].call(depth0, "view.optionGroupPath", {hash:{},inverse:self.program(6, program6, data),fn:self.program(3, program3, data),contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data});
if(stack1 || stack1 === 0) { data.buffer.push(stack1); } if(stack1 || stack1 === 0) { data.buffer.push(stack1); }
return buffer; return buffer;
}), }),
attributeBindings: ['multiple', 'disabled', 'tabindex', 'name'], attributeBindings: ['multiple', 'disabled', 'tabindex', 'name'],
@ -24951,12 +24946,12 @@ define("router",
A Transition is a thennable (a promise-like object) that represents A Transition is a thennable (a promise-like object) that represents
an attempt to transition to another route. It can be aborted, either an attempt to transition to another route. It can be aborted, either
explicitly via `abort` or by attempting another transition while a explicitly via `abort` or by attempting another transition while a
previous one is still underway. An aborted transition can also previous one is still underway. An aborted transition can also
be `retry()`d later. be `retry()`d later.
*/ */
function Transition(router, promise) { function Transition(router, promise) {
this.router = router; this.router = router;
this.promise = promise; this.promise = promise;
this.data = {}; this.data = {};
@ -24980,9 +24975,9 @@ define("router",
The Transition's internal promise. Calling `.then` on this property The Transition's internal promise. Calling `.then` on this property
is that same as calling `.then` on the Transition object itself, but is that same as calling `.then` on the Transition object itself, but
this property is exposed for when you want to pass around a this property is exposed for when you want to pass around a
Transition's promise, but not the Transition object itself, since Transition's promise, but not the Transition object itself, since
Transition object can be externally `abort`ed, while the promise Transition object can be externally `abort`ed, while the promise
cannot. cannot.
*/ */
promise: null, promise: null,
@ -24996,12 +24991,12 @@ define("router",
data: null, data: null,
/** /**
A standard promise hook that resolves if the transition A standard promise hook that resolves if the transition
succeeds and rejects if it fails/redirects/aborts. succeeds and rejects if it fails/redirects/aborts.
Forwards to the internal `promise` property which you can Forwards to the internal `promise` property which you can
use in situations where you want to pass around a thennable, use in situations where you want to pass around a thennable,
but not the Transition itself. but not the Transition itself.
@param {Function} success @param {Function} success
@param {Function} failure @param {Function} failure
@ -25012,18 +25007,18 @@ define("router",
/** /**
Aborts the Transition. Note you can also implicitly abort a transition Aborts the Transition. Note you can also implicitly abort a transition
by initiating another transition while a previous one is underway. by initiating another transition while a previous one is underway.
*/ */
abort: function() { abort: function() {
if (this.isAborted) { return this; } if (this.isAborted) { return this; }
log(this.router, this.sequence, this.targetName + ": transition was aborted"); log(this.router, this.sequence, this.targetName + ": transition was aborted");
this.isAborted = true; this.isAborted = true;
this.router.activeTransition = null; this.router.activeTransition = null;
return this; return this;
}, },
/** /**
Retries a previously-aborted transition (making sure to abort the Retries a previously-aborted transition (making sure to abort the
transition if it's still active). Returns a new transition that transition if it's still active). Returns a new transition that
represents the new attempt to transition. represents the new attempt to transition.
*/ */
@ -25037,7 +25032,7 @@ define("router",
}, },
/** /**
Sets the URL-changing method to be employed at the end of a Sets the URL-changing method to be employed at the end of a
successful transition. By default, a new Transition will just successful transition. By default, a new Transition will just
use `updateURL`, but passing 'replace' to this method will use `updateURL`, but passing 'replace' to this method will
cause the URL to update using 'replaceWith' instead. Omitting cause the URL to update using 'replaceWith' instead. Omitting
@ -25070,12 +25065,12 @@ define("router",
handlers for failed transitions. handlers for failed transitions.
*/ */
Router.UnrecognizedURLError = function(message) { Router.UnrecognizedURLError = function(message) {
this.message = (message || "UnrecognizedURLError"); this.message = (message || "UnrecognizedURLError");
this.name = "UnrecognizedURLError"; this.name = "UnrecognizedURLError";
}; };
Router.TransitionAborted = function(message) { Router.TransitionAborted = function(message) {
this.message = (message || "TransitionAborted"); this.message = (message || "TransitionAborted");
this.name = "TransitionAborted"; this.name = "TransitionAborted";
}; };
@ -25242,8 +25237,8 @@ define("router",
if (isParam(object)) { if (isParam(object)) {
var recogHandler = recogHandlers[i], name = recogHandler.names[0]; var recogHandler = recogHandlers[i], name = recogHandler.names[0];
if (object.toString() !== this.currentParams[name]) { return false; } if (object.toString() !== this.currentParams[name]) { return false; }
} else if (handlerInfo.context !== object) { } else if (handlerInfo.context !== object) {
return false; return false;
} }
} }
} }
@ -25274,7 +25269,7 @@ define("router",
*/ */
function getMatchPoint(router, handlers, objects, inputParams) { function getMatchPoint(router, handlers, objects, inputParams) {
var matchPoint = handlers.length, var matchPoint = handlers.length,
providedModels = {}, i, providedModels = {}, i,
currentHandlerInfos = router.currentHandlerInfos || [], currentHandlerInfos = router.currentHandlerInfos || [],
params = {}, params = {},
@ -25285,9 +25280,9 @@ define("router",
objects = slice.call(objects); objects = slice.call(objects);
merge(params, inputParams); merge(params, inputParams);
for (i = handlers.length - 1; i >= 0; i--) { for (i = handlers.length - 1; i >= 0; i--) {
var handlerObj = handlers[i], var handlerObj = handlers[i],
handlerName = handlerObj.handler, handlerName = handlerObj.handler,
oldHandlerInfo = currentHandlerInfos[i], oldHandlerInfo = currentHandlerInfos[i],
hasChanged = false; hasChanged = false;
@ -25325,7 +25320,7 @@ define("router",
handlerParams[handlerName][name] = params[name] = params[name] || oldParams[name]; handlerParams[handlerName][name] = params[name] = params[name] || oldParams[name];
} }
} }
} }
if (hasChanged) { matchPoint = i; } if (hasChanged) { matchPoint = i; }
} }
@ -25353,7 +25348,7 @@ define("router",
// Use model from previous transition attempt, preferably the resolved one. // Use model from previous transition attempt, preferably the resolved one.
return (paramName && activeTransition.providedModels[handlerName]) || return (paramName && activeTransition.providedModels[handlerName]) ||
activeTransition.resolvedModels[handlerName]; activeTransition.resolvedModels[handlerName];
} }
} }
function isParam(object) { function isParam(object) {
@ -25523,7 +25518,7 @@ define("router",
if (handler.setup) { handler.setup(context); } if (handler.setup) { handler.setup(context); }
checkAbort(transition); checkAbort(transition);
} catch(e) { } catch(e) {
if (!(e instanceof Router.TransitionAborted)) { if (!(e instanceof Router.TransitionAborted)) {
// Trigger the `error` event starting from this failed handler. // Trigger the `error` event starting from this failed handler.
trigger(currentHandlerInfos.concat(handlerInfo), true, ['error', e, transition]); trigger(currentHandlerInfos.concat(handlerInfo), true, ['error', e, transition]);
} }
@ -25673,11 +25668,11 @@ define("router",
wasTransitioning = false; wasTransitioning = false;
// Check if there's already a transition underway. // Check if there's already a transition underway.
if (router.activeTransition) { if (router.activeTransition) {
if (transitionsIdentical(router.activeTransition, targetName, providedModelsArray)) { if (transitionsIdentical(router.activeTransition, targetName, providedModelsArray)) {
return router.activeTransition; return router.activeTransition;
} }
router.activeTransition.abort(); router.activeTransition.abort();
wasTransitioning = true; wasTransitioning = true;
} }
@ -25733,8 +25728,8 @@ define("router",
@private @private
Accepts handlers in Recognizer format, either returned from Accepts handlers in Recognizer format, either returned from
recognize() or handlersFor(), and returns unified recognize() or handlersFor(), and returns unified
`HandlerInfo`s. `HandlerInfo`s.
*/ */
function generateHandlerInfos(router, recogHandlers) { function generateHandlerInfos(router, recogHandlers) {
var handlerInfos = []; var handlerInfos = [];
@ -25798,7 +25793,7 @@ define("router",
router.currentParams = params; router.currentParams = params;
var urlMethod = transition.urlMethod; var urlMethod = transition.urlMethod;
if (urlMethod) { if (urlMethod) {
var url = router.recognizer.generate(handlerName, params); var url = router.recognizer.generate(handlerName, params);
if (urlMethod === 'replace') { if (urlMethod === 'replace') {
@ -25874,12 +25869,12 @@ define("router",
log(router, seq, handlerName + ": handling error: " + reason); log(router, seq, handlerName + ": handling error: " + reason);
// An error was thrown / promise rejected, so fire an // An error was thrown / promise rejected, so fire an
// `error` event from this handler info up to root. // `error` event from this handler info up to root.
trigger(handlerInfos.slice(0, index + 1), true, ['error', reason, transition]); trigger(handlerInfos.slice(0, index + 1), true, ['error', reason, transition]);
if (handler.error) { if (handler.error) {
handler.error(reason, transition); handler.error(reason, transition);
} }
// Propagate the original error. // Propagate the original error.
@ -25929,7 +25924,7 @@ define("router",
Throws a TransitionAborted if the provided transition has been aborted. Throws a TransitionAborted if the provided transition has been aborted.
*/ */
function checkAbort(transition) { function checkAbort(transition) {
if (transition.isAborted) { if (transition.isAborted) {
log(transition.router, transition.sequence, "detected abort."); log(transition.router, transition.sequence, "detected abort.");
throw new Router.TransitionAborted(); throw new Router.TransitionAborted();
} }
@ -25959,7 +25954,7 @@ define("router",
} }
/** /**
@private @private
*/ */
function log(router, sequence, msg) { function log(router, sequence, msg) {
@ -26018,7 +26013,7 @@ define("router",
// Use custom serialize if it exists. // Use custom serialize if it exists.
if (handler.serialize) { if (handler.serialize) {
return handler.serialize(model, names); return handler.serialize(model, names);
} }
if (names.length !== 1) { return; } if (names.length !== 1) { return; }
@ -28563,7 +28558,7 @@ Ember.ControllerMixin.reopen({
Optionally supply a model for the route in question. The model Optionally supply a model for the route in question. The model
will be serialized into the URL using the `serialize` hook of will be serialized into the URL using the `serialize` hook of
the route: the route:
```javascript ```javascript
aController.transitionToRoute('blogPost', aPost); aController.transitionToRoute('blogPost', aPost);
``` ```
@ -28687,7 +28682,7 @@ Ember.View.reopen({
// Add a new named queue after the 'actions' queue (where RSVP promises // Add a new named queue after the 'actions' queue (where RSVP promises
// resolve), which is used in router transitions to prevent unnecessary // resolve), which is used in router transitions to prevent unnecessary
// loading state entry if all context promises resolve on the // loading state entry if all context promises resolve on the
// 'actions' queue first. // 'actions' queue first.
var queues = Ember.run.queues, var queues = Ember.run.queues,
@ -32156,7 +32151,7 @@ function chain(app, promise, fn) {
* using your app. * using your app.
* *
* Example: * Example:
* *
* ``` * ```
* visit('posts/index').then(function() { * visit('posts/index').then(function() {
* // assert something * // assert something
@ -32164,7 +32159,7 @@ function chain(app, promise, fn) {
* ``` * ```
* *
* @method visit * @method visit
* @param {String} url the name of the route * @param {String} url the name of the route
* @returns {RSVP.Promise} * @returns {RSVP.Promise}
*/ */
helper('visit', visit); helper('visit', visit);

View File

@ -17156,12 +17156,6 @@ Ember.View = Ember.CoreView.extend(
return viewCollection; return viewCollection;
}, },
_elementWillChange: Ember.beforeObserver(function() {
this.forEachChildView(function(view) {
Ember.propertyWillChange(view, 'element');
});
}, 'element'),
/** /**
@private @private
@ -17173,7 +17167,8 @@ Ember.View = Ember.CoreView.extend(
*/ */
_elementDidChange: Ember.observer(function() { _elementDidChange: Ember.observer(function() {
this.forEachChildView(function(view) { this.forEachChildView(function(view) {
Ember.propertyDidChange(view, 'element'); var meta = Em.meta(view);
delete meta.cache['element'];
}); });
}, 'element'), }, 'element'),
@ -20561,17 +20556,13 @@ var DOMManager = {
view.clearRenderedChildren(); view.clearRenderedChildren();
var buffer = view.renderToBuffer(); var buffer = view.renderToBuffer();
// view.invokeRecursively(function(view) { view.propertyWillChange('element');
// view.propertyWillChange('element');
// });
view.triggerRecursively('willInsertElement'); view.triggerRecursively('willInsertElement');
morph.replaceWith(buffer.string()); morph.replaceWith(buffer.string());
view.transitionTo('inDOM'); view.transitionTo('inDOM');
// view.invokeRecursively(function(view) { view.propertyDidChange('element');
// view.propertyDidChange('element');
// });
view.triggerRecursively('didInsertElement'); view.triggerRecursively('didInsertElement');
notifyMutationListeners(); notifyMutationListeners();