--- a/web/data/cubicweb.ajax.js Mon Jan 04 18:40:30 2016 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,743 +0,0 @@
-/* copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
- * contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
- *
- * This file is part of CubicWeb.
- *
- * CubicWeb is free software: you can redistribute it and/or modify it under the
- * terms of the GNU Lesser General Public License as published by the Free
- * Software Foundation, either version 2.1 of the License, or (at your option)
- * any later version.
- *
- * CubicWeb is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
- * details.
- *
- * You should have received a copy of the GNU Lesser General Public License along
- * with CubicWeb. If not, see <http://www.gnu.org/licenses/>.
- */
-
-/**
- * .. function:: Deferred
- *
- * dummy ultra minimalist implementation of deferred for jQuery
- */
-
-cw.ajax = new Namespace('cw.ajax');
-
-function Deferred() {
- this.__init__(this);
-}
-
-jQuery.extend(Deferred.prototype, {
- __init__: function() {
- this._onSuccess = [];
- this._onFailure = [];
- this._req = null;
- this._result = null;
- this._error = null;
- },
-
- addCallback: function(callback) {
- if (this._req && (this._req.readyState == 4) && this._result) {
- var args = [this._result, this._req];
- jQuery.merge(args, cw.utils.sliceList(arguments, 1));
- callback.apply(null, args);
- }
- else {
- this._onSuccess.push([callback, cw.utils.sliceList(arguments, 1)]);
- }
- return this;
- },
-
- addErrback: function(callback) {
- if (this._req && this._req.readyState == 4 && this._error) {
- callback.apply(null, [this._error, this._req]);
- }
- else {
- this._onFailure.push([callback, cw.utils.sliceList(arguments, 1)]);
- }
- return this;
- },
-
- success: function(result) {
- this._result = result;
- for (var i = 0; i < this._onSuccess.length; i++) {
- var callback = this._onSuccess[i][0];
- var args = [result, this._req];
- jQuery.merge(args, this._onSuccess[i][1]);
- callback.apply(null, args);
- }
- },
-
- error: function(xhr, status, error) {
- this._error = error;
- for (var i = 0; i < this._onFailure.length; i++) {
- var callback = this._onFailure[i][0];
- var args = [error, this._req];
- jQuery.merge(args, this._onFailure[i][1]);
- if (callback !== undefined)
- callback.apply(null, args);
- }
- }
-
-});
-
-var AJAX_PREFIX_URL = 'ajax';
-var JSON_BASE_URL = BASE_URL + 'json?';
-var AJAX_BASE_URL = BASE_URL + AJAX_PREFIX_URL + '?';
-
-
-jQuery.extend(cw.ajax, {
- /* variant of jquery evalScript with cache: true in ajax call */
- _evalscript: function ( i, elem ) {
- var src = elem.getAttribute('src');
- if (src) {
- jQuery.ajax({
- url: src,
- async: false,
- cache: true,
- dataType: "script"
- });
- } else {
- jQuery.globalEval( elem.text || elem.textContent || elem.innerHTML || "" );
- }
- if ( elem.parentNode ) {
- elem.parentNode.removeChild( elem );
- }
- },
-
- evalscripts: function ( scripts ) {
- if ( scripts.length ) {
- jQuery.each(scripts, cw.ajax._evalscript);
- }
- },
-
- /**
- * returns true if `url` is a mod_concat-like url
- * (e.g. http://..../data??resource1.js,resource2.js)
- */
- _modconcatLikeUrl: function(url) {
- var modconcat_rgx = new RegExp('(' + BASE_URL + 'data/([a-z0-9]+/)?)\\?\\?(.+)');
- return modconcat_rgx.exec(url);
- },
-
- /**
- * decomposes a mod_concat-like url into its corresponding list of
- * resources' urls
- * >>> _listResources('http://foo.com/data/??a.js,b.js,c.js')
- * ['http://foo.com/data/a.js', 'http://foo.com/data/b.js', 'http://foo.com/data/c.js']
- */
- _listResources: function(src) {
- var resources = [];
- var groups = cw.ajax._modconcatLikeUrl(src);
- if (groups == null) {
- resources.push(src);
- } else {
- var dataurl = groups[1];
- $.each(cw.utils.lastOf(groups).split(','),
- function() {
- resources.push(dataurl + this);
- }
- );
- }
- return resources;
- },
-
- _buildMissingResourcesUrl: function(url, loadedResources) {
- var resources = cw.ajax._listResources(url);
- var missingResources = $.grep(resources, function(resource) {
- return $.inArray(resource, loadedResources) == -1;
- });
- cw.utils.extend(loadedResources, missingResources);
- var missingResourceUrl = null;
- if (missingResources.length == 1) {
- // only one resource missing: build a node with a single resource url
- // (maybe the browser has it in cache already)
- missingResourceUrl = missingResources[0];
- } else if (missingResources.length > 1) {
- // several resources missing: build a node with a concatenated
- // resources url
- var dataurl = cw.ajax._modconcatLikeUrl(url)[1];
- var missing_path = $.map(missingResources, function(resource) {
- return resource.substring(dataurl.length);
- });
- missingResourceUrl = dataurl + '??' + missing_path.join(',');
- }
- return missingResourceUrl;
- },
-
- _loadAjaxStylesheets: function($responseHead, $head) {
- $responseHead.find('link[href]').each(function(i) {
- var $srcnode = $(this);
- var url = $srcnode.attr('href');
- if (url) {
- var missingStylesheetsUrl = cw.ajax._buildMissingResourcesUrl(url, cw.loaded_links);
- // compute concat-like url for missing resources and append <link>
- // element to $head
- if (missingStylesheetsUrl) {
- // IE has problems with dynamic CSS insertions. One symptom (among others)
- // is a "1 item remaining" message in the status bar. (cf. #2356261)
- // document.createStyleSheet needs to be used for this, although it seems
- // that IE can't create more than 31 additional stylesheets with
- // document.createStyleSheet.
- if ($.browser.msie) {
- document.createStyleSheet(missingStylesheetsUrl);
- } else {
- $srcnode.attr('href', missingStylesheetsUrl);
- $srcnode.appendTo($head);
- }
- }
- }
- });
- $responseHead.find('link[href]').remove();
- },
-
- _loadAjaxScripts: function($responseHead, $head) {
- $responseHead.find('cubicweb\\:script').each(function(i) {
- var $srcnode = $(this);
- var url = $srcnode.attr('src');
- if (url) {
- var missingScriptsUrl = cw.ajax._buildMissingResourcesUrl(url, cw.loaded_scripts);
- if (missingScriptsUrl) {
- $srcnode.attr('src', missingScriptsUrl);
- /* special handling of <script> tags: script nodes appended by jquery
- * use uncached ajax calls and do not appear in the DOM
- * (See comments in response to Syt on // http://api.jquery.com/append/),
- * which cause undesired duplicated load in our case. We now handle
- * a list of already loaded resources, since bare DOM api gives bugs with the
- * server-response event, and we lose control on when the
- * script is loaded (jQuery loads it immediately). */
- cw.ajax.evalscripts($srcnode);
- }
- } else {
- // <script> contains inlined javascript code, node content
- // must be evaluated
- jQuery.globalEval($srcnode.text());
- }
- });
- $responseHead.find('cubicweb\\:script').remove();
- }
-});
-
-//============= utility function handling remote calls responses. ==============//
-/**
- * .. function:: function loadAjaxHtmlHead(response)
- *
- * inspect dom response (as returned by getDomFromResponse), search for
- * a <div class="ajaxHtmlHead"> node and put its content into the real
- * document's head.
- * This enables dynamic css and js loading and is used by replacePageChunk
- */
-function loadAjaxHtmlHead(response) {
- var $head = jQuery('head');
- var $responseHead = jQuery(response).find('div.ajaxHtmlHead');
- // no ajaxHtmlHead found, no processing required
- if (!$responseHead.length) {
- return response;
- }
- cw.ajax._loadAjaxStylesheets($responseHead, $head);
- cw.ajax._loadAjaxScripts($responseHead, $head);
- // add any remaining children (e.g. meta)
- $responseHead.children().appendTo($head);
- // remove original container, which is now empty
- $responseHead.remove();
- // if there was only one actual node in the reponse besides
- // the ajaxHtmlHead, then remove the wrapper created by
- // getDomFromResponse() and return this single element
- // For instance :
- // 1/ CW returned the following content :
- // <div>the-actual-content</div><div class="ajaxHtmlHead">...</div>
- // 2/ getDomFromReponse() wrapped this into a single DIV to hold everything
- // in one, unique, dom element
- // 3/ now that we've removed the ajaxHtmlHead div, the only
- // node left in the wrapper if the 'real' node built by the view,
- // we can safely return this node. Otherwise, the view itself
- // returned several 'root' nodes and we need to keep the wrapper
- // created by getDomFromResponse()
- if (response.childNodes.length == 1 && response.getAttribute('cubicweb:type') == 'cwResponseWrapper') {
- return response.firstChild;
- }
- return response;
-}
-
-function _postAjaxLoad(node) {
- // find textareas and wrap them if there are some
- if (typeof(FCKeditor) != 'undefined') {
- buildWysiwygEditors();
- }
- if (typeof initFacetBoxEvents != 'undefined') {
- initFacetBoxEvents(node);
- }
- if (typeof buildWidgets != 'undefined') {
- buildWidgets(node);
- }
- if (typeof roundedCorners != 'undefined') {
- roundedCorners(node);
- }
- _loadDynamicFragments(node);
- jQuery(cw).trigger('server-response', [true, node]);
- jQuery(node).trigger('server-response', [true, node]);
-}
-
-function remoteCallFailed(err, req) {
- cw.log(err);
- if (req.status == 500) {
- updateMessage(err);
- } else {
- updateMessage(_("an error occurred while processing your request"));
- }
-}
-
-//============= base AJAX functions to make remote calls =====================//
-/**
- * .. function:: ajaxFuncArgs(fname, form, *args)
- *
- * extend `form` parameters to call the js_`fname` function of the json
- * controller with `args` arguments.
- */
-function ajaxFuncArgs(fname, form /* ... */) {
- form = form || {};
- $.extend(form, {
- 'fname': fname,
- 'pageid': pageid,
- 'arg': $.map(cw.utils.sliceList(arguments, 2), JSON.stringify)
- });
- return form;
-}
-
-/**
- * .. function:: loadxhtml(url, form, reqtype='get', mode='replace', cursor=false)
- *
- * build url given by absolute or relative `url` and `form` parameters
- * (dictionary), fetch it using `reqtype` method, then evaluate the
- * returned XHTML and insert it according to `mode` in the
- * document. Possible modes are :
- *
- * - 'replace' to replace the node's content with the generated HTML
- * - 'swap' to replace the node itself with the generated HTML
- * - 'append' to append the generated HTML to the node's content
- *
- * If `cursor`, turn mouse cursor into 'progress' cursor until the remote call
- * is back.
- */
-jQuery.fn.loadxhtml = function(url, form, reqtype, mode, cursor) {
- if (this.size() > 1) {
- cw.log('loadxhtml called with more than one element');
- } else if (this.size() < 1) {
- cw.log('loadxhtml called without an element');
- }
- var node = this.get(0); // only consider the first element
- if (cursor) {
- setProgressCursor();
- }
- var d = loadRemote(url, form, reqtype);
- d.addCallback(function(response) {
- var domnode = getDomFromResponse(response);
- domnode = loadAjaxHtmlHead(domnode);
- mode = mode || 'replace';
- // make sure the component is visible
- $(node).removeClass("hidden");
- if (mode == 'swap') {
- var origId = node.id;
- node = cw.swapDOM(node, domnode);
- if (!node.id) {
- node.id = origId;
- }
- } else if (mode == 'replace') {
- jQuery(node).empty().append(domnode);
- } else if (mode == 'append') {
- jQuery(node).append(domnode);
- }
- _postAjaxLoad(node);
- });
- d.addErrback(remoteCallFailed);
- if (cursor) {
- d.addCallback(resetCursor);
- d.addErrback(resetCursor);
- }
- return d;
-}
-
-/**
- * .. function:: loadRemote(url, form, reqtype='POST', sync=false)
- *
- * Asynchronously (unless `sync` argument is set to true) load a URL or path
- * and return a deferred whose callbacks args are decoded according to the
- * Content-Type response header. `form` should be additional form params
- * dictionary, `reqtype` the HTTP request type (get 'GET' or 'POST').
- */
-function loadRemote(url, form, reqtype, sync) {
- if (!url.toLowerCase().startswith(BASE_URL.toLowerCase())) {
- url = BASE_URL + url;
- }
- if (!sync) {
- var deferred = new Deferred();
- jQuery.ajax({
- url: url,
- type: (reqtype || 'POST').toUpperCase(),
- data: form,
- traditional: true,
- async: true,
-
- beforeSend: function(xhr) {
- deferred._req = xhr;
- },
-
- success: function(data, status) {
- deferred.success(data);
- },
-
- error: function(xhr, status, error) {
- try {
- if (xhr.status == 500) {
- var reason_dict = cw.evalJSON(xhr.responseText);
- deferred.error(xhr, status, reason_dict['reason']);
- return;
- }
- } catch(exc) {
- cw.log('error with server side error report:' + exc);
- }
- deferred.error(xhr, status, null);
- }
- });
- return deferred;
- } else {
- var result;
- // jQuery.ajax returns the XHR object, even for synchronous requests,
- // but in that case, the success callback will be called before
- // jQuery.ajax returns. The first argument of the callback will be
- // the server result, interpreted by jQuery according to the reponse's
- // content-type (i.e. json or xml)
- jQuery.ajax({
- url: url,
- type: (reqtype || 'GET').toUpperCase(),
- data: form,
- traditional: true,
- async: false,
- success: function(res) {
- result = res;
- }
- });
- return result;
- }
-}
-
-//============= higher level AJAX functions using remote calls ===============//
-
-var _i18ncache = {};
-
-/**
- * .. function:: _(message)
- *
- * emulation of gettext's _ shortcut
- */
-function _(message) {
- var form;
- if (!(message in _i18ncache)) {
- form = ajaxFuncArgs('i18n', null, [message]);
- _i18ncache[message] = loadRemote(AJAX_BASE_URL, form, 'GET', true)[0];
- }
- return _i18ncache[message];
-}
-
-/**
- * .. function:: _loadDynamicFragments(node)
- *
- * finds each dynamic fragment in the page and executes the
- * the associated RQL to build them (Async call)
- */
-function _loadDynamicFragments(node) {
- if (node) {
- var fragments = jQuery(node).find('div.dynamicFragment');
- } else {
- var fragments = jQuery('div.dynamicFragment');
- }
- if (fragments.length == 0) {
- return;
- }
- if (typeof LOADING_MSG == 'undefined') {
- LOADING_MSG = 'loading'; // this is only a safety belt, it should not happen
- }
- for (var i = 0; i < fragments.length; i++) {
- var fragment = fragments[i];
- fragment.innerHTML = '<h3>' + LOADING_MSG + ' ... <img src="data/loading.gif" /></h3>';
- var $fragment = jQuery(fragment);
- // if cubicweb:loadurl is set, just pick the url et send it to loadxhtml
- var url = $fragment.attr('cubicweb:loadurl');
- if (url) {
- $fragment.loadxhtml(url);
- continue;
- }
- // else: rebuild full url by fetching cubicweb:rql, cubicweb:vid, etc.
- var rql = $fragment.attr('cubicweb:rql');
- var items = $fragment.attr('cubicweb:vid').split('&');
- var vid = items[0];
- var extraparams = {};
- // case where vid='myvid¶m1=val1¶m2=val2': this is a deprecated abuse-case
- if (items.length > 1) {
- cw.log("[3.5] you're using extraargs in cubicweb:vid " +
- "attribute, this is deprecated, consider using " +
- "loadurl instead");
- for (var j = 1; j < items.length; j++) {
- var keyvalue = items[j].split('=');
- extraparams[keyvalue[0]] = keyvalue[1];
- }
- }
- var actrql = $fragment.attr('cubicweb:actualrql');
- if (actrql) {
- extraparams['actualrql'] = actrql;
- }
- var fbvid = $fragment.attr('cubicweb:fallbackvid');
- if (fbvid) {
- extraparams['fallbackvid'] = fbvid;
- }
- extraparams['rql'] = rql;
- extraparams['vid'] = vid;
- $fragment.loadxhtml(AJAX_BASE_URL, ajaxFuncArgs('view', extraparams));
- }
-}
-function unloadPageData() {
- // NOTE: do not make async calls on unload if you want to avoid
- // strange bugs
- loadRemote(AJAX_BASE_URL, ajaxFuncArgs('unload_page_data'), 'POST', true);
-}
-
-function removeBookmark(beid) {
- var d = loadRemote(AJAX_BASE_URL, ajaxFuncArgs('delete_bookmark', null, beid));
- d.addCallback(function(boxcontent) {
- $('#bookmarks_box').loadxhtml(AJAX_BASE_URL,
- ajaxFuncArgs('render', null, 'ctxcomponents',
- 'bookmarks_box'),
- null, 'swap');
- document.location.hash = '#header';
- updateMessage(_("bookmark has been removed"));
- });
-}
-
-
-//============= XXX move those functions? ====================================//
-function openHash() {
- if (document.location.hash) {
- var nid = document.location.hash.replace('#', '');
- var node = jQuery('#' + nid);
- if (node) {
- $(node).removeClass("hidden");
- }
- };
-}
-jQuery(document).ready(openHash);
-
-/**
- * .. function:: buildWysiwygEditors(parent)
- *
- *XXX: this function should go in edition.js but as for now, htmlReplace
- * references it.
- *
- * replace all textareas with fckeditors.
- */
-function buildWysiwygEditors(parent) {
- jQuery('textarea').each(function() {
- if (this.getAttribute('cubicweb:type') == 'wysiwyg') {
- // mark editor as instanciated, we may be called a number of times
- // (see _postAjaxLoad)
- this.setAttribute('cubicweb:type', 'fckeditor');
- if (typeof FCKeditor != "undefined") {
- var fck = new FCKeditor(this.id);
- fck.Config['CustomConfigurationsPath'] = fckconfigpath;
- fck.Config['DefaultLanguage'] = fcklang;
- fck.BasePath = BASE_URL + "fckeditor/";
- fck.ReplaceTextarea();
- } else {
- cw.log('fckeditor could not be found.');
- }
- }
- });
-}
-jQuery(document).ready(buildWysiwygEditors);
-
-/**
- * .. function:: stripEmptyTextNodes(nodelist)
- *
- * takes a list of DOM nodes and removes all empty text nodes
- */
-function stripEmptyTextNodes(nodelist) {
- /* this DROPS empty text nodes */
- var stripped = [];
- for (var i = 0; i < nodelist.length; i++) {
- var node = nodelist[i];
- if (isTextNode(node)) {
- /* all browsers but FF -> innerText, FF -> textContent */
- var text = node.innerText || node.textContent;
- if (text && ! text.strip()) {
- continue;
- }
- } else {
- stripped.push(node);
- }
- }
- return stripped;
-}
-
-/**
- * .. function:: getDomFromResponse(response)
- *
- * convenience function that returns a DOM node based on req's result.
- * XXX clarify the need to clone
- * */
-function getDomFromResponse(response) {
- if (typeof(response) == 'string') {
- var doc = html2dom(response);
- } else {
- var doc = response.documentElement;
- }
- var children = doc.childNodes;
- if (!children.length) {
- // no child (error cases) => return the whole document
- return jQuery(doc).clone().context;
- }
- children = stripEmptyTextNodes(children);
- if (children.length == 1) {
- // only one child => return it
- return jQuery(children[0]).clone().context;
- }
- // several children => wrap them in a single node and return the wrap
- return DIV({'cubicweb:type': "cwResponseWrapper"},
- $.map(children, function(node) {
- return jQuery(node).clone().context;})
- );
-}
-
-/* High-level functions *******************************************************/
-
-/**
- * .. function:: reloadCtxComponentsSection(context, actualEid, creationEid=None)
- *
- * reload all components in the section for a given `context`.
- *
- * This is necessary for cases where the parent entity (on which the section
- * apply) has been created during post, hence the section has to be reloaded to
- * consider its new eid, hence the two additional arguments `actualEid` and
- * `creationEid`: `actualEid` is the eid of newly created top level entity and
- * `creationEid` the fake eid that was given as form creation marker (e.g. A).
- *
- * You can still call this function with only the actual eid if you're not in
- * such creation case.
- */
-function reloadCtxComponentsSection(context, actualEid, creationEid) {
- // in this case, actualEid is the eid of newly created top level entity and
- // creationEid the fake eid given as form creation marker (e.g. A)
- if (!creationEid) { creationEid = actualEid ; }
- var $compsholder = $('#' + context + creationEid);
- // reload the whole components section
- $compsholder.children().each(function (index) {
- // XXX this.id[:-len(eid)]
- var compid = this.id.replace("_", ".").rstrip(creationEid);
- var params = ajaxFuncArgs('render', null, 'ctxcomponents',
- compid, actualEid);
- $(this).loadxhtml(AJAX_BASE_URL, params, null, 'swap', true);
- });
- $compsholder.attr('id', context + actualEid);
-}
-
-
-/**
- * .. function:: reload(domid, compid, registry, formparams, *render_args)
- *
- * `js_render` based reloading of views and components.
- */
-function reload(domid, compid, registry, formparams /* ... */) {
- var ajaxArgs = ['render', formparams, registry, compid];
- ajaxArgs = ajaxArgs.concat(cw.utils.sliceList(arguments, 4));
- var params = ajaxFuncArgs.apply(null, ajaxArgs);
- return $('#'+domid).loadxhtml(AJAX_BASE_URL, params, null, 'swap', true);
-}
-
-/* ajax tabs ******************************************************************/
-
-function setTab(tabname, cookiename) {
- // set appropriate cookie
- jQuery.cookie(cookiename, tabname, {path: '/'});
- // trigger show + tabname event
- triggerLoad(tabname);
-}
-
-function loadNow(eltsel, holesel, reloadable) {
- var lazydiv = jQuery(eltsel);
- var hole = lazydiv.children(holesel);
- hole.show();
- if ((hole.length == 0) && ! reloadable) {
- /* the hole is already filed */
- return;
- }
- lazydiv.loadxhtml(lazydiv.attr('cubicweb:loadurl'), {'pageid': pageid});
-}
-
-function triggerLoad(divid) {
- jQuery('#lazy-' + divid).trigger('load_' + divid);
-}
-
-/* DEPRECATED *****************************************************************/
-
-// still used in cwo and keyword cubes at least
-reloadComponent = cw.utils.deprecatedFunction(
- '[3.9] reloadComponent() is deprecated, use loadxhtml instead',
- function(compid, rql, registry, nodeid, extraargs) {
- registry = registry || 'components';
- rql = rql || '';
- nodeid = nodeid || (compid + 'Component');
- extraargs = extraargs || {};
- var node = cw.jqNode(nodeid);
- return node.loadxhtml(AJAX_BASE_URL, ajaxFuncArgs('component', null, compid,
- rql, registry, extraargs));
- }
-);
-
-
-function remoteExec(fname /* ... */) {
- setProgressCursor();
- var props = {
- fname: fname,
- pageid: pageid,
- arg: $.map(cw.utils.sliceList(arguments, 1), JSON.stringify)
- };
- var result = jQuery.ajax({
- url: AJAX_BASE_URL,
- data: props,
- async: false,
- traditional: true
- }).responseText;
- if (result) {
- result = cw.evalJSON(result);
- }
- resetCursor();
- return result;
-}
-
-function asyncRemoteExec(fname /* ... */) {
- setProgressCursor();
- var props = {
- fname: fname,
- pageid: pageid,
- arg: $.map(cw.utils.sliceList(arguments, 1), JSON.stringify)
- };
- // XXX we should inline the content of loadRemote here
- var deferred = loadRemote(AJAX_BASE_URL, props, 'POST');
- deferred = deferred.addErrback(remoteCallFailed);
- deferred = deferred.addErrback(resetCursor);
- deferred = deferred.addCallback(resetCursor);
- return deferred;
-}
-
-jQuery(document).ready(function() {
- _loadDynamicFragments();
- // build loaded_scripts / loaded_links lists
- cw.loaded_scripts = [];
- jQuery('head script[src]').each(function(i) {
- cw.utils.extend(cw.loaded_scripts, cw.ajax._listResources(this.getAttribute('src')));
- });
- cw.loaded_links = [];
- jQuery('head link[href]').each(function(i) {
- cw.utils.extend(cw.loaded_links, cw.ajax._listResources(this.getAttribute('href')));
- });
-});