|
|
@@ -1,8 +1,8 @@
|
|
|
/*
|
|
|
---
|
|
|
-description: Any JFrame response that has a root-level child element with the class .partial_refresh will find all elements that have a property defined for data-partial-id that is unique to the response and only update them. If there is a mismatch in the response such that the number of and ids of partials in the previous state do not match the return state, an alert will be shown the user that the entire view will be updated that they can cancel, if they so choose.
|
|
|
+description: Any JFrame response that has elements that have a property defined for data-partial-id will have each auto-refresh response only update them.
|
|
|
provides: [CCS.JFrame.PartialRefresh]
|
|
|
-requires: [/CCS.JFrame, Widgets/ART.Alerts, More/Table, Widgets/Element.Data]
|
|
|
+requires: [/CCS.JFrame, Widgets/ART.Alerts, More/Table, Widgets/Element.Data, /PartialUpdate]
|
|
|
script: CCS.JFrame.PartialRefresh.js
|
|
|
|
|
|
...
|
|
|
@@ -24,8 +24,6 @@ script: CCS.JFrame.PartialRefresh.js
|
|
|
// limitations under the License.
|
|
|
(function(){
|
|
|
|
|
|
- var enableLog; //set to true if you want to log messages; left here for convenience.
|
|
|
-
|
|
|
CCS.JFrame.addGlobalRenderers({
|
|
|
|
|
|
/*
|
|
|
@@ -35,16 +33,14 @@ script: CCS.JFrame.PartialRefresh.js
|
|
|
to the previous response. When there's a mis-match in the response, only those elements that have changed
|
|
|
are run through the JFrame filters to configure them for display and then replace their expired counterparts.
|
|
|
|
|
|
- Each area that may have elements that should be checked is identified with the class "partial_refresh" on some
|
|
|
- container; often a div wrapping the entire response.
|
|
|
-
|
|
|
- Each individual item that might be refreshed must be marked with a data-partial-id property that is unique
|
|
|
- to the JFrame (i.e. multiple app windows can have id overlap).
|
|
|
+ Each individual item that might be refreshed must be marked with a data-single-partial-id property that is unique
|
|
|
+ to the JFrame (i.e. multiple app windows can have id overlap) OR have a data-partial-id property and be the
|
|
|
+ child of a container with app-unique data-partial-container-id.
|
|
|
|
|
|
If you need to support the loading of additional content after the fact, such as with loading additional Ajax
|
|
|
elements within the frame or if the response may return more or fewer items than the previous response, then
|
|
|
you must add some additional instrumentation to your HTML. Specifically, you *must* define a container where
|
|
|
- new elements are injected. This is typically the immediate parent of the partials, like so:
|
|
|
+ new elements are injected. This MUST be the immediate parent of the partials, like so:
|
|
|
|
|
|
Example w/ just partials and container
|
|
|
<div data-partial-container-id="div1">
|
|
|
@@ -52,8 +48,10 @@ script: CCS.JFrame.PartialRefresh.js
|
|
|
<p data-partial-id="p2">bar</p>
|
|
|
</div>
|
|
|
|
|
|
- In some cases, such as with tables, you may wish to add "lines" between the partials and the container. A good
|
|
|
- example here is a table which has partials for individual td elements, but new tr elements may come and go.
|
|
|
+ The only exception to this requirement, such as with tables, you may wish to add "lines" between
|
|
|
+ the partials and the container. A good example here is a table which has partials for individual
|
|
|
+ td elements, but new tr elements may come and go. In this case the "lines" must be immediate children
|
|
|
+ of the parent, but the individual partials need not be immediate children of the lines.
|
|
|
|
|
|
Example w/ partials, container, and lines
|
|
|
<tbody data-partial-container-id="tbody1">
|
|
|
@@ -71,6 +69,24 @@ script: CCS.JFrame.PartialRefresh.js
|
|
|
the previous row would be found there. It's id is "tr1". The *live* DOM tree - the one the user sees - would be searched
|
|
|
for an element with that line-id. The new line would be injected after it.
|
|
|
|
|
|
+ It is possible to support the possibility of the server returning a new sort order. This is a little more expensive
|
|
|
+ so it must be "turned on" by giving the container the class name "ordered_partial_refresh".
|
|
|
+
|
|
|
+ Example w/ partials, container, and lines that will obey sort order.
|
|
|
+ <tbody data-partial-container-id="tbody1" class="ordered_partial_refresh">
|
|
|
+ <tr data-partial-line-id="tr1">
|
|
|
+ <td data-partial-id="id1"></td>
|
|
|
+ <td data-partial-id="id2"></td>
|
|
|
+ </tr>
|
|
|
+ <tr data-partial-line-id="tr2">
|
|
|
+ <td data-partial-id="id3"></td>
|
|
|
+ <td data-partial-id="id4"></td>
|
|
|
+ </tr>
|
|
|
+ </tbody>
|
|
|
+
|
|
|
+ In the above example, if the server replied with tr2 preceding tr1, the order would update in the view because
|
|
|
+ of the "ordered_partial_refresh" class.
|
|
|
+
|
|
|
When using ajax to load in new content (such as with the AjaxLoad linker) you'll likely load new content in ouside
|
|
|
the scope of this refresh handler. I.e. you'll handle the response and inject the new items yourself. In this case
|
|
|
partial refresh needs only to store the original request state for future checksums. To accomplish this, set the
|
|
|
@@ -80,128 +96,127 @@ script: CCS.JFrame.PartialRefresh.js
|
|
|
*/
|
|
|
|
|
|
partialRefresh: function(content){
|
|
|
+ dbug.timeEnd('partial refresh');
|
|
|
+ dbug.time('partial refresh');
|
|
|
var options = content.options || {};
|
|
|
//if the request options include the ignorePartialRequest flag, exit
|
|
|
if (options.ignorePartialRefresh) return;
|
|
|
//when we load content via ajax, we don't want the response being parsed for partials
|
|
|
var jState = getJState(this);
|
|
|
//get the partial containers; containers that have elements in them to be partially refreshed
|
|
|
- var partialContainers = new Element('div').adopt(content.elements).getElements('.partial_refresh');
|
|
|
+ var elements = new Element('div').adopt(content.elements);
|
|
|
+ //find all the containers in the response that have data-partial-container-id
|
|
|
+ var partialContainers = elements.getElements('[data-partial-container-id]');
|
|
|
+
|
|
|
+ dbug.time('create');
|
|
|
+ //and either fetch or create instances of PartialUpdate for them
|
|
|
+ var updaters = partialContainers.map(function(container){
|
|
|
+ return {
|
|
|
+ updater: getUpdater(container, jState, {
|
|
|
+ //when an element is destroyed, call jframe's garbage collection method
|
|
|
+ onElementDestroy: function(el){
|
|
|
+ this.collectElement(el);
|
|
|
+ }.bind(this),
|
|
|
+ //before individual elements are inserted in the DOM, run them through the jframe filters
|
|
|
+ onBeforeUpdate: function(checked){
|
|
|
+ dbug.time('update:filters');
|
|
|
+ this.applyFilters(checked.target);
|
|
|
+ dbug.timeEnd('update:filters');
|
|
|
+ }.bind(this)
|
|
|
+ }),
|
|
|
+ responseContainer: container
|
|
|
+ };
|
|
|
+ }, this);
|
|
|
+ dbug.timeEnd('create');
|
|
|
+
|
|
|
+ //find stand-alone partials; these have no containers
|
|
|
+ var standAlones = elements.getElements('[data-single-partial-id]'),
|
|
|
+ tmpTarget = new Element('div');
|
|
|
+ dbug.time('stand alone');
|
|
|
+
|
|
|
+ for (var i = 0; i < standAlones.length; i++){
|
|
|
+ var element = standAlones[i];
|
|
|
+ var id = element.get('data-single-partial-id');
|
|
|
+ if (!jState.standAlones[id]){
|
|
|
+ //create an instance of the single partial updater for each of these
|
|
|
+ jState.standAlones[id] = new PartialUpdate.Single(element, {
|
|
|
+ onElementDestroy: function(el){
|
|
|
+ this.collectElement(el);
|
|
|
+ }.bind(this),
|
|
|
+ partialIdProperty: 'data-single-partial-id',
|
|
|
+ updateStateOnStartup: false,
|
|
|
+ onBeforeUpdate: function(element){
|
|
|
+ dbug.time('update:filters');
|
|
|
+ this.applyFilters(tmpTarget.adopt(element));
|
|
|
+ dbug.timeEnd('update:filters');
|
|
|
+ }.bind(this)
|
|
|
+ });
|
|
|
+ }
|
|
|
+ updaters.push({
|
|
|
+ updater: jState.standAlones[id],
|
|
|
+ responseContainer: elements
|
|
|
+ });
|
|
|
+ }
|
|
|
+ dbug.timeEnd('stand alone');
|
|
|
+
|
|
|
+ //this method just updates the state of each updater to match that of the response
|
|
|
+ var updateState = function(){
|
|
|
+ dbug.time('update state');
|
|
|
+ updaters.each(function(updaterObj){
|
|
|
+ updaterObj.updater.updateState(updaterObj.responseContainer);
|
|
|
+ });
|
|
|
+ dbug.timeEnd('update state');
|
|
|
+ };
|
|
|
|
|
|
//for requests that want to handle the element injection themselves (ajaxload, for example)
|
|
|
//they can instruct this filter to process the partials in the response and then exit
|
|
|
if (options.onlyProcessPartials) {
|
|
|
- processPartials(content, jState);
|
|
|
+ updateState();
|
|
|
return;
|
|
|
}
|
|
|
//store the previous path to be the current one
|
|
|
jState.prevPath = options ? options.responsePath : null;
|
|
|
|
|
|
- if (!partialContainers.length) {
|
|
|
- if (enableLog) dbug.log('no partials to refresh, exiting');
|
|
|
+ if (!updaters.length) {
|
|
|
//no partial containers, reset and fall through to other renderers
|
|
|
- jState.partials = null;
|
|
|
+ jState.updaters = {};
|
|
|
+ jState.standAlones = {};
|
|
|
this.enableSpinnerUsage();
|
|
|
return;
|
|
|
}
|
|
|
- //get the partials in the containers
|
|
|
- var partialContainer = new Element('div').adopt(partialContainers),
|
|
|
- //we need a copy of this for later when we sort stuff
|
|
|
- untouchedPartialContainer = partialContainer.clone(true, true),
|
|
|
- //get the partials from the container
|
|
|
- partials = getPartials(partialContainer, true);
|
|
|
|
|
|
- //if the options aren't defined or if we didn't auto refresh, reset and
|
|
|
- //return (fall through to other renderers)
|
|
|
+ //disable spinner for the next autorefresh
|
|
|
+ this.disableSpinnerUsage();
|
|
|
+
|
|
|
+ //if the options aren't defined or if we didn't auto refresh fall through to other renderers
|
|
|
if (!options || !(options.autorefreshed || options.forcePartial)) {
|
|
|
//...then store the state and render as usual (fall through to other renderers)
|
|
|
- if (enableLog) dbug.log('not auto refreshed (%s), or new path (%s != %s) and not forced (%s), existing partial refresh after setup', !options.autorefreshed, jState.prevPath, options.responsePath, options.forcePartial);
|
|
|
- jState.partials = partials;
|
|
|
- this.disableSpinnerUsage();
|
|
|
+ updateState();
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- if (new URI(options.requestPath).toString() != new URI(options.responsePath).toString()) {
|
|
|
- if (enableLog) dbug.warn('detected partial refresh on a possible redirect (the request path (%s) != the response path (%s)), continuing with partial refresh', new URI(options.requestPath).toString(), new URI(options.responsePath).toString());
|
|
|
- }
|
|
|
-
|
|
|
- //don't show the spinner for partial refreshes
|
|
|
- this.disableSpinnerUsage();
|
|
|
-
|
|
|
/*******************************
|
|
|
FORM HERE ON OUT
|
|
|
this filter will handle the response; we return true and other renderers are excluded
|
|
|
UNLESS there is a partial returned that we cannot find the proper place to put it
|
|
|
(i.e. it has no partial-container).
|
|
|
*******************************/
|
|
|
- if (enableLog) dbug.log('proceeding with partial refresh');
|
|
|
//store the path as the current one
|
|
|
this.currentPath = options.responsePath || this.currentPath;
|
|
|
|
|
|
- var checkedPartials = checkPartials(partials, jState);
|
|
|
-
|
|
|
- //render the content
|
|
|
- if (!checkedPartials.renderedPartials.length) {
|
|
|
- if (enableLog) dbug.log('no partials for render; exiting quietly');
|
|
|
- //if we aren't updating anything, that's cool, but still call the autorefresh filter
|
|
|
- //to ensure that the frame keeps refreshing
|
|
|
- this.applyFilter('autorefresh', new Element('div'), content);
|
|
|
- //if there is no new content, return true (so no other renderers are called)
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- //apply all the jframe magic to our filtered content
|
|
|
- if (enableLog) dbug.log('applying filters');
|
|
|
-
|
|
|
- //it's faster to render this stuff outside the live DOM, apparently
|
|
|
- //so store the scroll offset and pop it out, leaving a placeholder
|
|
|
- var container = $(this);
|
|
|
- var scroll = container.getParent().getScroll();
|
|
|
- var s = new Element('span').inject(container, 'after');
|
|
|
- container.dispose();
|
|
|
+ dbug.time('update');
|
|
|
+ updaters.each(function(updaterObj){
|
|
|
+ updaterObj.updater.update(updaterObj.responseContainer);
|
|
|
+ });
|
|
|
+ dbug.timeEnd('update');
|
|
|
+ dbug.time('apply filters to content');
|
|
|
+ this.applyFilters(new Element('div'), content);
|
|
|
+ dbug.timeEnd('apply filters to content');
|
|
|
|
|
|
-
|
|
|
- //filters expect to be handed an object (content) that has various properties of the
|
|
|
- //response; the meta tags, the script tags, and elements that were in the body, etc.
|
|
|
- //because we want to run the elements we're updating through the filters, we overwrite
|
|
|
- //the elements that were passed in to this renderer with only the elements we want
|
|
|
- //updated and then apply the filters.
|
|
|
- content.elements = checkedPartials.renderedPartials;
|
|
|
- this.applyFilters(checkedPartials.target, content);
|
|
|
- //now loop through the partials again and inject them into the DOM structure from the response
|
|
|
- //replacing the original partial with the cloned one
|
|
|
- restorePartials(checkedPartials.partialClones, partials);
|
|
|
- //finally put the partials into the real DOM
|
|
|
- var success = injectPartials(partials, jState, checkedPartials.partialClones, this.behavior, container);
|
|
|
-
|
|
|
- if (!success) {
|
|
|
- //if there was a failure, we put the container back and exit to the normal renderer
|
|
|
- s.parentNode.replaceChild(container, s);
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- //sort the contents in the "real" container with the order from the response
|
|
|
- sortPartials(untouchedPartialContainer, container);
|
|
|
-
|
|
|
- //remove any partials that were in the previous response but not in the recent one
|
|
|
- cleanRemainingParitals(partials, jState, this.behavior);
|
|
|
-
|
|
|
- //put the container back in the live DOM
|
|
|
- s.parentNode.replaceChild(container, s);
|
|
|
- container.getParent().scrollTo(scroll.x, scroll.y);
|
|
|
//we've updated the display, so tell filters that are waiting that they may need to update their display, too
|
|
|
this.behavior.fireEvent('show');
|
|
|
//prevent other renderers from handling the response
|
|
|
-
|
|
|
-
|
|
|
- //after a second, remove the updated designation
|
|
|
- (function(){
|
|
|
- var els = $(this).getElements('.partial-refresh-updated');
|
|
|
- for (var i = 0; i < els.length; i++){
|
|
|
- els[i].removeClass('partial-refresh-updated');
|
|
|
- }
|
|
|
- }).delay(1000, this);
|
|
|
-
|
|
|
+ dbug.timeEnd('partial refresh');
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
@@ -213,310 +228,25 @@ script: CCS.JFrame.PartialRefresh.js
|
|
|
var getJState = function(jframe) {
|
|
|
var jState = jframeStates.get(jframe);
|
|
|
if (!jState) {
|
|
|
- jState = {};
|
|
|
+ jState = {
|
|
|
+ updaters: {},
|
|
|
+ standAlones: {}
|
|
|
+ };
|
|
|
jframeStates.set(jframe, jState);
|
|
|
}
|
|
|
return jState;
|
|
|
};
|
|
|
|
|
|
- //gets all the partial elements to refresh from the specified container
|
|
|
- //if *store* == true, then store this state on each element as the original,
|
|
|
- //unaltered response
|
|
|
- var getPartials = function(container, store) {
|
|
|
- var partials = {};
|
|
|
- //if there are no partial elements, return now
|
|
|
- if (!container.innerHTML.contains('data-partial-id')) return partials;
|
|
|
- //get all the elements with a partial id
|
|
|
- var partial,
|
|
|
- els = container.getElements('[data-partial-id]');
|
|
|
- for (var i = 0; i < els.length; i++){
|
|
|
- partial = els[i];
|
|
|
- //store a pointer to that element to return it
|
|
|
- partials[partial.getAttribute('data-partial-id')] = partial;
|
|
|
- //if instructed to, store the original state of the response before it was altered by any filter
|
|
|
- if (store) partial.store('partialRefresh:unaltered', partial.innerHTML);
|
|
|
- }
|
|
|
- return partials;
|
|
|
- };
|
|
|
-
|
|
|
- var processPartials = function(content, jState){
|
|
|
- var toProcess = getPartials(new Element('div').adopt(content.elements), true);
|
|
|
- //if there are partials already, merge the result
|
|
|
- if (jState.partials) {
|
|
|
- for (id in toProcess) {
|
|
|
- jState.partials[id] = toProcess[id].store('partialRefresh:unaltered', toProcess[id].innerHTML);
|
|
|
- }
|
|
|
- //else there were no partials in the previous response; so assign these.
|
|
|
- } else {
|
|
|
- jState.partials = toProcess;
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- var checkPartials = function(partials, jState){
|
|
|
- var data = {
|
|
|
- renderedPartials: new Elements(),
|
|
|
- partialClones: {},
|
|
|
- target: new Element('div')
|
|
|
- };
|
|
|
- //loop through the partials and figure out which ones need updating so that we can
|
|
|
- //run only those through the filters
|
|
|
- for (id in partials){
|
|
|
- var partial = partials[id];
|
|
|
- if (enableLog) dbug.log('considering %s for update', id);
|
|
|
- //get the corresponding element in the dom
|
|
|
- var before = jState.partials[id];
|
|
|
- //if there isn't one, or thier raw html don't match, we'll update it, so we must render it
|
|
|
- if (!before || !compare(before, partial)) {
|
|
|
- if (enableLog) dbug.log('preparing %s for update', id);
|
|
|
- //we must preserve the DOM structure to be able to find partial containers and partial lines
|
|
|
- //so clone the partial for rendering
|
|
|
- var clone = document.id(partial.cloneNode(true));
|
|
|
- data.target.appendChild(clone);
|
|
|
- //we need an array of these elements for when we call applyFilters below, which expects
|
|
|
- //content.elements to be an array of the elements returned in the response
|
|
|
- data.renderedPartials.push(clone);
|
|
|
- //we also need a key/value map of all the clones for quick lookups when we put them back
|
|
|
- //into the response DOM
|
|
|
- data.partialClones[id] = clone;
|
|
|
- }
|
|
|
- }
|
|
|
- return data;
|
|
|
- };
|
|
|
-
|
|
|
- //given a partial, attempts to find the line it is in
|
|
|
- //example: for a td that is a partial, it may have the tr as its line
|
|
|
- var getPartialLine = function(partial){
|
|
|
- return partial.getParent('[data-partial-line-id]');
|
|
|
- };
|
|
|
- //given a partial, attempts to find the container it is in
|
|
|
- //for example, for a td that is a partial, it may have the tr as its line and the table as its container
|
|
|
- var getPartialContainers = function(partial, container){
|
|
|
- var containers = {
|
|
|
- container: partial.getParent('[data-partial-container-id]')
|
|
|
- };
|
|
|
- if (containers.container) {
|
|
|
- containers.DOMcontainer = getMatchFromDOM(containers.container, container, 'data-partial-container-id');
|
|
|
- }
|
|
|
- return containers;
|
|
|
- };
|
|
|
-
|
|
|
- //given two partials, compares their raw HTML before they were parsed by filters
|
|
|
- var compare = function(before, after){
|
|
|
- return before.retrieve('partialRefresh:unaltered') == after.retrieve('partialRefresh:unaltered');
|
|
|
- };
|
|
|
-
|
|
|
- //this method destroys a partial given its partial id
|
|
|
- var destroyPartial = function(id, jState, behavior){
|
|
|
- //get the element
|
|
|
- var element = jState.partials[id];
|
|
|
-
|
|
|
- clean(element, behavior);
|
|
|
- //delete it from the jState
|
|
|
- delete jState.partials[id];
|
|
|
- };
|
|
|
-
|
|
|
- //this method takes a group of cloned partials (that have been passed through filters)
|
|
|
- //and puts them back into the DOM from which they came, replacing the elements they
|
|
|
- //were cloned from. This allows us to retain the DOM structure of the response, while
|
|
|
- //only running through the filters the elements that need it.
|
|
|
- var restorePartials = function(clones, partials){
|
|
|
- var count = 0;
|
|
|
- for (id in clones) {
|
|
|
- if (enableLog) dbug.log('replacing target with clone: ', id);
|
|
|
- var clone = clones[id],
|
|
|
- partial = partials[id];
|
|
|
- //because we're replacing, we need to copy over thier original HTML state for the checksum
|
|
|
- clone.store('partialRefresh:unaltered', partial.retrieve('partialRefresh:unaltered'));
|
|
|
- count++;
|
|
|
- partial.parentNode.replaceChild(clone, partial);
|
|
|
- //and then update the pointer as the clone is now the rendered partial
|
|
|
- partials[id] = clone;
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- var injectPartials = function(partials, jState, rendered, behavior, container) {
|
|
|
- var insertedPartials = {},
|
|
|
- partial,
|
|
|
- prevId;
|
|
|
- if (enableLog) dbug.log('iterating over partials for injection');
|
|
|
- //iterate over all the partials to inject them into the live DOM
|
|
|
- var result = true;
|
|
|
- for (id in partials) {
|
|
|
- partial = partials[id];
|
|
|
- if (enableLog) dbug.log('considering %s for injection', id);
|
|
|
- //if it's in a line that's been injected, skip it
|
|
|
- //if it was passed through the renderers, it means that it needs an update or insertion
|
|
|
- if (rendered[id] && !insertedPartials[id]) {
|
|
|
- //get the corresponding partial in the DOM
|
|
|
- var before = jState.partials[id];
|
|
|
- //if there's a corresponding partial already in the DOM, replace it
|
|
|
- if (before) {
|
|
|
- if (enableLog) dbug.log('performing update for %s', id);
|
|
|
- // partial.addClass('partial-refresh-updated partial-refresh-transitionable').replaces(before);
|
|
|
- partial.addClass('partial-refresh-updated partial-refresh-transitionable').replaces(before);
|
|
|
- clean(before, behavior);
|
|
|
- } else {
|
|
|
- //else it's not in the DOM
|
|
|
- //look to see if this partial is in a line item (for example, the tr for a td that is a partial)
|
|
|
- var line = getPartialLine(partial);
|
|
|
- //if there is no line, inject it into the DOM in the container
|
|
|
- var success;
|
|
|
- if (line) success = injectPartialLine(partial, line, container, insertedPartials);
|
|
|
- else success = injectPartial(jState, partial, prevId, container);
|
|
|
- if (!success) {
|
|
|
- result = false;
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- if (rendered[id]) jState.partials[id] = partial;
|
|
|
- prevId = id;
|
|
|
- }
|
|
|
- return result;
|
|
|
- };
|
|
|
-
|
|
|
- //given a line, destroy it
|
|
|
- var destroyLine = function(line, behavior){
|
|
|
- if (enableLog) dbug.log('destroying line:', line);
|
|
|
- behavior.cleanup(line);
|
|
|
- line.destroy();
|
|
|
- };
|
|
|
-
|
|
|
- var cleanRemainingParitals = function(partials, jState, behavior){
|
|
|
- var linesToDestroy = {};
|
|
|
- //for any partials that were in the DOM but not in the response, remove them
|
|
|
- for (id in jState.partials) {
|
|
|
- partial = jState.partials[id];
|
|
|
- //if the partial is in the DOM but not the response
|
|
|
- if (!partials[id]) {
|
|
|
- //get its line; assume that we have to remove that, too
|
|
|
- var line = getPartialLine(partial);
|
|
|
- if (line) linesToDestroy[line.getAttribute('data-partial-line-id')] = line;
|
|
|
- if (enableLog) dbug.log('destroying %s', id, line);
|
|
|
- //destroy the partial
|
|
|
- destroyPartial(id, jState, behavior);
|
|
|
- }
|
|
|
- }
|
|
|
- for (id in linesToDestroy) {
|
|
|
- destroyLine(linesToDestroy[id], behavior);
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- var injectPartial = function(jState, partial, prevId, container){
|
|
|
- var previousPartial = jState.partials[prevId];
|
|
|
- if (previousPartial) {
|
|
|
- if (enableLog) dbug.log('injecting line for %s after previous item (%s)', id, previousPartial);
|
|
|
- //if this isn't the first one, inject it after the previous id
|
|
|
- partial.inject(previousPartial, 'after');
|
|
|
- } else {
|
|
|
- //find the container and inject it as the first item there
|
|
|
- var containers = getPartialContainers(partial, container);
|
|
|
- if (containers.DOMcontainer) {
|
|
|
- if (enableLog) dbug.log('injecting %s into top of container (%o)', id, containers.DOMcontainer);
|
|
|
- partial.inject(containers.DOMcontainer, 'top');
|
|
|
- } else {
|
|
|
- //else, we don't know where to inject it
|
|
|
- dbug.warn('Could not inject partial (%o); no container or previous item found.', partial);
|
|
|
- return false;
|
|
|
- }
|
|
|
- }
|
|
|
- return true;
|
|
|
- };
|
|
|
-
|
|
|
- var injectPartialLine = function(partial, line, container, insertedPartials){
|
|
|
- if (enableLog) dbug.log('preparing line for injection');
|
|
|
- //there is a line, so we inject it instead of the partial.
|
|
|
- //get the previous line (from the response)
|
|
|
- var prevLine = line.getPrevious('[data-partial-line-id]'),
|
|
|
- prevLineInDOM;
|
|
|
-
|
|
|
- //now find it's counterpart in the live DOM
|
|
|
- if (prevLine) prevLineInDOM = container.getElement('[data-partial-line-id=' + prevLine.getAttribute('data-partial-line-id') + ']');
|
|
|
- //if it's there, inject this line after it
|
|
|
- if (prevLineInDOM) {
|
|
|
- if (enableLog) dbug.log('injecting line (%o) after previous line (%o)', line, prevLine);
|
|
|
- line.inject(prevLineInDOM, 'after');
|
|
|
- } else {
|
|
|
- //else this is the first line, so inject it at the top of the container
|
|
|
- var lineContainers = getPartialContainers(partial, container);
|
|
|
- if (lineContainers.DOMcontainer) {
|
|
|
- if (enableLog) dbug.log('injecting line (%o) into top of container (%o)', line, lineContainers.DOMcontainer);
|
|
|
- line.inject(lineContainers.DOMcontainer, 'top');
|
|
|
- } else {
|
|
|
- //else, we don't know where to inject it
|
|
|
- dbug.warn('Could not inject partial (%o) in line (%o); no container or previous item found.', partial, line);
|
|
|
- return false;
|
|
|
- }
|
|
|
- }
|
|
|
- //store the fact that we just injected all the partials in this line
|
|
|
- line.getElements('[data-partial-id]').each(function(p){
|
|
|
- p.addClass('partial-refresh-updated partial-refresh-transitionable');
|
|
|
- insertedPartials[p.getAttribute('data-partial-id')] = true;
|
|
|
- });
|
|
|
- return true;
|
|
|
- };
|
|
|
-
|
|
|
- var getMatchFromDOM = function(element, DOM, property){
|
|
|
- return DOM.getElement('[' + property + '=' + element.getAttribute(property) + ']');
|
|
|
- };
|
|
|
-
|
|
|
- var getPartialId = function(el){
|
|
|
- return el.getAttribute('data-partial-line-id') || el.getAttribute('data-partial-id');
|
|
|
- };
|
|
|
-
|
|
|
-
|
|
|
- var sortPartials = function(partialsContainer, container) {
|
|
|
- partialsContainer.getElements('.ordered_partial_refresh').each(function(ordered){
|
|
|
- var DOMtwin = getMatchFromDOM(ordered, container, 'data-partial-container-id');
|
|
|
- var kids = ordered.getChildren('[data-partial-line-id]', '[data-partial-id]');
|
|
|
- var DOMkidIds = {};
|
|
|
- var DOMkids = DOMtwin.getChildren('[data-partial-line-id]', '[data-partial-id]');
|
|
|
- for (var DOMkid = 0; DOMkid < DOMkids.length; DOMkid++){
|
|
|
- DOMkidIds[getPartialId(DOMkids[DOMkid])] = DOMkids[DOMkid];
|
|
|
- }
|
|
|
- for (var kid = 0; kid < kids.length; kid++){
|
|
|
- var id = getPartialId(kids[kid]);
|
|
|
- DOMkidIds[id].inject(DOMtwin);
|
|
|
- }
|
|
|
- });
|
|
|
- };
|
|
|
-
|
|
|
- var toGC = [], gcTimer;
|
|
|
- var clean = function(element, behavior){
|
|
|
- if (!gcTimer) gcTimer = gc.periodical(100);
|
|
|
- toGC.push({
|
|
|
- element: element, behavior: behavior
|
|
|
- });
|
|
|
- };
|
|
|
-
|
|
|
- //this is a "threadsafe" garbage collection routine
|
|
|
- //if we call element.destroy on, say, 1000 nodes, the MooTools gc goes through
|
|
|
- //all of them at once. This version does them in batches, releasing the thread
|
|
|
- //as it goes.
|
|
|
- var gc = function(){
|
|
|
- if (toGC.length) {
|
|
|
- var i = 0;
|
|
|
- if (enableLog) dbug.log("removing %s of %s elements", Math.max(100, toGC.length/10), toGC.length);
|
|
|
- while (toGC.length && i < Math.max(100, toGC.length/100)) {
|
|
|
- var data = toGC.shift();
|
|
|
- //clean up its behaviors
|
|
|
- data.behavior.cleanup(data.element);
|
|
|
- //destroy the element
|
|
|
- destroy(data.element);
|
|
|
- i++;
|
|
|
- }
|
|
|
- if (enableLog) dbug.info('remaining elements to clean: ', toGC.length);
|
|
|
- if (i == 0) clearInterval(gcTimer);
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- //implementing a custom destroy method; this is slightly faster than the MooTools version
|
|
|
- //but doesn't do some of it's less-important garbage collection
|
|
|
- var destroy = function(el){
|
|
|
- for (var i = 0; i < el.childNodes.length; i++) {
|
|
|
- destroy(el.childNodes[i]);
|
|
|
+ var getUpdater = function(container, jState, options){
|
|
|
+ var id = container.get('data-partial-container-id');
|
|
|
+ if (!jState.updaters[id]) {
|
|
|
+ jState.updaters[id] = new PartialUpdate(container, $merge({
|
|
|
+ sorted: container.hasClass('ordered_partial_refresh'),
|
|
|
+ clone: container.get('html').contains('data-filter'),
|
|
|
+ updateStateOnStartup: false
|
|
|
+ }, options));
|
|
|
}
|
|
|
- if (el.removeEvents) el.removeEvents();
|
|
|
+ return jState.updaters[id];
|
|
|
};
|
|
|
|
|
|
})();
|