You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
665 lines
23 KiB
665 lines
23 KiB
define([ |
|
"dojo/_base/kernel", // kernel.deprecated |
|
"dojo/_base/lang", // lang.mixin lang.delegate lang.hitch lang.isFunction lang.isObject |
|
"../_Widget", |
|
"../_Container", |
|
"./_ContentPaneResizeMixin", |
|
"dojo/string", // string.substitute |
|
"dojo/html", // html._ContentSetter |
|
"dojo/_base/array", // array.forEach |
|
"dojo/_base/declare", // declare |
|
"dojo/_base/Deferred", // Deferred |
|
"dojo/dom", // dom.byId |
|
"dojo/dom-attr", // domAttr.attr |
|
"dojo/dom-construct", // empty() |
|
"dojo/_base/xhr", // xhr.get |
|
"dojo/i18n", // i18n.getLocalization |
|
"dojo/when", |
|
"dojo/i18n!../nls/loading" |
|
], function(kernel, lang, _Widget, _Container, _ContentPaneResizeMixin, string, html, array, declare, |
|
Deferred, dom, domAttr, domConstruct, xhr, i18n, when){ |
|
|
|
// module: |
|
// dijit/layout/ContentPane |
|
|
|
return declare("dijit.layout.ContentPane", [_Widget, _Container, _ContentPaneResizeMixin], { |
|
// summary: |
|
// A widget containing an HTML fragment, specified inline |
|
// or by uri. Fragment may include widgets. |
|
// |
|
// description: |
|
// This widget embeds a document fragment in the page, specified |
|
// either by uri, javascript generated markup or DOM reference. |
|
// Any widgets within this content are instantiated and managed, |
|
// but laid out according to the HTML structure. Unlike IFRAME, |
|
// ContentPane embeds a document fragment as would be found |
|
// inside the BODY tag of a full HTML document. It should not |
|
// contain the HTML, HEAD, or BODY tags. |
|
// For more advanced functionality with scripts and |
|
// stylesheets, see dojox/layout/ContentPane. This widget may be |
|
// used stand alone or as a base class for other widgets. |
|
// ContentPane is useful as a child of other layout containers |
|
// such as BorderContainer or TabContainer, but note that those |
|
// widgets can contain any widget as a child. |
|
// |
|
// example: |
|
// Some quick samples: |
|
// To change the innerHTML: |
|
// | cp.set('content', '<b>new content</b>')` |
|
// Or you can send it a NodeList: |
|
// | cp.set('content', dojo.query('div [class=selected]', userSelection)) |
|
// To do an ajax update: |
|
// | cp.set('href', url) |
|
|
|
// href: String |
|
// The href of the content that displays now. |
|
// Set this at construction if you want to load data externally when the |
|
// pane is shown. (Set preload=true to load it immediately.) |
|
// Changing href after creation doesn't have any effect; Use set('href', ...); |
|
href: "", |
|
|
|
// content: String|DomNode|NodeList|dijit/_Widget |
|
// The innerHTML of the ContentPane. |
|
// Note that the initialization parameter / argument to set("content", ...) |
|
// can be a String, DomNode, Nodelist, or _Widget. |
|
content: "", |
|
|
|
// extractContent: Boolean |
|
// Extract visible content from inside of `<body> .... </body>`. |
|
// I.e., strip `<html>` and `<head>` (and it's contents) from the href |
|
extractContent: false, |
|
|
|
// parseOnLoad: Boolean |
|
// Parse content and create the widgets, if any. |
|
parseOnLoad: true, |
|
|
|
// parserScope: String |
|
// Flag passed to parser. Root for attribute names to search for. If scopeName is dojo, |
|
// will search for data-dojo-type (or dojoType). For backwards compatibility |
|
// reasons defaults to dojo._scopeName (which is "dojo" except when |
|
// multi-version support is used, when it will be something like dojo16, dojo20, etc.) |
|
parserScope: kernel._scopeName, |
|
|
|
// preventCache: Boolean |
|
// Prevent caching of data from href's by appending a timestamp to the href. |
|
preventCache: false, |
|
|
|
// preload: Boolean |
|
// Force load of data on initialization even if pane is hidden. |
|
preload: false, |
|
|
|
// refreshOnShow: Boolean |
|
// Refresh (re-download) content when pane goes from hidden to shown |
|
refreshOnShow: false, |
|
|
|
// loadingMessage: String |
|
// Message that shows while downloading |
|
loadingMessage: "<span class='dijitContentPaneLoading'><span class='dijitInline dijitIconLoading'></span>${loadingState}</span>", |
|
|
|
// errorMessage: String |
|
// Message that shows if an error occurs |
|
errorMessage: "<span class='dijitContentPaneError'><span class='dijitInline dijitIconError'></span>${errorState}</span>", |
|
|
|
// isLoaded: [readonly] Boolean |
|
// True if the ContentPane has data in it, either specified |
|
// during initialization (via href or inline content), or set |
|
// via set('content', ...) / set('href', ...) |
|
// |
|
// False if it doesn't have any content, or if ContentPane is |
|
// still in the process of downloading href. |
|
isLoaded: false, |
|
|
|
baseClass: "dijitContentPane", |
|
|
|
/*====== |
|
// ioMethod: dojo/_base/xhr.get|dojo._base/xhr.post |
|
// Function that should grab the content specified via href. |
|
ioMethod: dojo.xhrGet, |
|
======*/ |
|
|
|
// ioArgs: Object |
|
// Parameters to pass to xhrGet() request, for example: |
|
// | <div data-dojo-type="dijit/layout/ContentPane" data-dojo-props="href: './bar', ioArgs: {timeout: 500}"> |
|
ioArgs: {}, |
|
|
|
// onLoadDeferred: [readonly] dojo.Deferred |
|
// This is the `dojo.Deferred` returned by set('href', ...) and refresh(). |
|
// Calling onLoadDeferred.then() registers your |
|
// callback to be called only once, when the prior set('href', ...) call or |
|
// the initial href parameter to the constructor finishes loading. |
|
// |
|
// This is different than an onLoad() handler which gets called any time any href |
|
// or content is loaded. |
|
onLoadDeferred: null, |
|
|
|
// Cancel _WidgetBase's _setTitleAttr because we don't want the title attribute (used to specify |
|
// tab labels) to be copied to ContentPane.domNode... otherwise a tooltip shows up over the |
|
// entire pane. |
|
_setTitleAttr: null, |
|
|
|
// Flag to parser that I'll parse my contents, so it shouldn't. |
|
stopParser: true, |
|
|
|
// template: [private] Boolean |
|
// Flag from the parser that this ContentPane is inside a template |
|
// so the contents are pre-parsed. |
|
// TODO: this declaration can be commented out in 2.0 |
|
template: false, |
|
|
|
markupFactory: function(params, node, ctor){ |
|
var self = new ctor(params, node); |
|
|
|
// If a parse has started but is waiting for modules to load, then return a Promise for when the parser |
|
// finishes. Don't return a promise though for the case when content hasn't started loading because the |
|
// ContentPane is hidden and it has an href (ex: hidden pane of a TabContainer). In that case we consider |
|
// that initialization has already finished. |
|
return !self.href && self._contentSetter && self._contentSetter.parseDeferred && !self._contentSetter.parseDeferred.isFulfilled() ? |
|
self._contentSetter.parseDeferred.then(function(){ |
|
return self; |
|
}) : self; |
|
}, |
|
|
|
create: function(params, srcNodeRef){ |
|
// Convert a srcNodeRef argument into a content parameter, so that the original contents are |
|
// processed in the same way as contents set via set("content", ...), calling the parser etc. |
|
// Avoid modifying original params object since that breaks NodeList instantiation, see #11906. |
|
if((!params || !params.template) && srcNodeRef && !("href" in params) && !("content" in params)){ |
|
srcNodeRef = dom.byId(srcNodeRef); |
|
var df = srcNodeRef.ownerDocument.createDocumentFragment(); |
|
while(srcNodeRef.firstChild){ |
|
df.appendChild(srcNodeRef.firstChild); |
|
} |
|
params = lang.delegate(params, {content: df}); |
|
} |
|
this.inherited(arguments, [params, srcNodeRef]); |
|
}, |
|
|
|
postMixInProperties: function(){ |
|
this.inherited(arguments); |
|
var messages = i18n.getLocalization("dijit", "loading", this.lang); |
|
this.loadingMessage = string.substitute(this.loadingMessage, messages); |
|
this.errorMessage = string.substitute(this.errorMessage, messages); |
|
}, |
|
|
|
buildRendering: function(){ |
|
this.inherited(arguments); |
|
|
|
// Since we have no template we need to set this.containerNode ourselves, to make getChildren() work. |
|
// For subclasses of ContentPane that do have a template, does nothing. |
|
if(!this.containerNode){ |
|
this.containerNode = this.domNode; |
|
} |
|
|
|
// remove the title attribute so it doesn't show up when hovering |
|
// over a node (TODO: remove in 2.0, no longer needed after #11490) |
|
this.domNode.removeAttribute("title"); |
|
}, |
|
|
|
startup: function(){ |
|
// summary: |
|
// Call startup() on all children including non _Widget ones like dojo/dnd/Source objects |
|
|
|
// This starts all the widgets |
|
this.inherited(arguments); |
|
|
|
// And this catches stuff like dojo/dnd/Source |
|
if(this._contentSetter){ |
|
array.forEach(this._contentSetter.parseResults, function(obj){ |
|
if(!obj._started && !obj._destroyed && lang.isFunction(obj.startup)){ |
|
obj.startup(); |
|
obj._started = true; |
|
} |
|
}, this); |
|
} |
|
}, |
|
|
|
_startChildren: function(){ |
|
// summary: |
|
// Called when content is loaded. Calls startup on each child widget. Similar to ContentPane.startup() |
|
// itself, but avoids marking the ContentPane itself as "restarted" (see #15581). |
|
|
|
// This starts all the widgets |
|
array.forEach(this.getChildren(), function(obj){ |
|
if(!obj._started && !obj._destroyed && lang.isFunction(obj.startup)){ |
|
obj.startup(); |
|
obj._started = true; |
|
} |
|
}); |
|
|
|
// And this catches stuff like dojo/dnd/Source |
|
if(this._contentSetter){ |
|
array.forEach(this._contentSetter.parseResults, function(obj){ |
|
if(!obj._started && !obj._destroyed && lang.isFunction(obj.startup)){ |
|
obj.startup(); |
|
obj._started = true; |
|
} |
|
}, this); |
|
} |
|
}, |
|
|
|
setHref: function(/*String|Uri*/ href){ |
|
// summary: |
|
// Deprecated. Use set('href', ...) instead. |
|
kernel.deprecated("dijit.layout.ContentPane.setHref() is deprecated. Use set('href', ...) instead.", "", "2.0"); |
|
return this.set("href", href); |
|
}, |
|
_setHrefAttr: function(/*String|Uri*/ href){ |
|
// summary: |
|
// Hook so set("href", ...) works. |
|
// description: |
|
// Reset the (external defined) content of this pane and replace with new url |
|
// Note: It delays the download until widget is shown if preload is false. |
|
// href: |
|
// url to the page you want to get, must be within the same domain as your mainpage |
|
|
|
// Cancel any in-flight requests (a set('href', ...) will cancel any in-flight set('href', ...)) |
|
this.cancel(); |
|
|
|
this.onLoadDeferred = new Deferred(lang.hitch(this, "cancel")); |
|
this.onLoadDeferred.then(lang.hitch(this, "onLoad")); |
|
|
|
this._set("href", href); |
|
|
|
// _setHrefAttr() is called during creation and by the user, after creation. |
|
// Assuming preload == false, only in the second case do we actually load the URL; |
|
// otherwise it's done in startup(), and only if this widget is shown. |
|
if(this.preload || (this._created && this._isShown())){ |
|
this._load(); |
|
}else{ |
|
// Set flag to indicate that href needs to be loaded the next time the |
|
// ContentPane is made visible |
|
this._hrefChanged = true; |
|
} |
|
|
|
return this.onLoadDeferred; // Deferred |
|
}, |
|
|
|
setContent: function(/*String|DomNode|Nodelist*/data){ |
|
// summary: |
|
// Deprecated. Use set('content', ...) instead. |
|
kernel.deprecated("dijit.layout.ContentPane.setContent() is deprecated. Use set('content', ...) instead.", "", "2.0"); |
|
this.set("content", data); |
|
}, |
|
_setContentAttr: function(/*String|DomNode|Nodelist*/data){ |
|
// summary: |
|
// Hook to make set("content", ...) work. |
|
// Replaces old content with data content, include style classes from old content |
|
// data: |
|
// the new Content may be String, DomNode or NodeList |
|
// |
|
// if data is a NodeList (or an array of nodes) nodes are copied |
|
// so you can import nodes from another document implicitly |
|
|
|
// clear href so we can't run refresh and clear content |
|
// refresh should only work if we downloaded the content |
|
this._set("href", ""); |
|
|
|
// Cancel any in-flight requests (a set('content', ...) will cancel any in-flight set('href', ...)) |
|
this.cancel(); |
|
|
|
// Even though user is just setting content directly, still need to define an onLoadDeferred |
|
// because the _onLoadHandler() handler is still getting called from setContent() |
|
this.onLoadDeferred = new Deferred(lang.hitch(this, "cancel")); |
|
if(this._created){ |
|
// For back-compat reasons, call onLoad() for set('content', ...) |
|
// calls but not for content specified in srcNodeRef (ie: <div data-dojo-type=ContentPane>...</div>) |
|
// or as initialization parameter (ie: new ContentPane({content: ...}) |
|
this.onLoadDeferred.then(lang.hitch(this, "onLoad")); |
|
} |
|
|
|
this._setContent(data || ""); |
|
|
|
this._isDownloaded = false; // mark that content is from a set('content') not a set('href') |
|
|
|
return this.onLoadDeferred; // Deferred |
|
}, |
|
_getContentAttr: function(){ |
|
// summary: |
|
// Hook to make get("content") work |
|
return this.containerNode.innerHTML; |
|
}, |
|
|
|
cancel: function(){ |
|
// summary: |
|
// Cancels an in-flight download of content |
|
if(this._xhrDfd && (this._xhrDfd.fired == -1)){ |
|
this._xhrDfd.cancel(); |
|
} |
|
delete this._xhrDfd; // garbage collect |
|
|
|
this.onLoadDeferred = null; |
|
}, |
|
|
|
destroy: function(){ |
|
this.cancel(); |
|
this.inherited(arguments); |
|
}, |
|
|
|
destroyRecursive: function(/*Boolean*/ preserveDom){ |
|
// summary: |
|
// Destroy the ContentPane and its contents |
|
|
|
// if we have multiple controllers destroying us, bail after the first |
|
if(this._beingDestroyed){ |
|
return; |
|
} |
|
this.inherited(arguments); |
|
}, |
|
|
|
_onShow: function(){ |
|
// summary: |
|
// Called when the ContentPane is made visible |
|
// description: |
|
// For a plain ContentPane, this is called on initialization, from startup(). |
|
// If the ContentPane is a hidden pane of a TabContainer etc., then it's |
|
// called whenever the pane is made visible. |
|
// |
|
// Does necessary processing, including href download and layout/resize of |
|
// child widget(s) |
|
|
|
this.inherited(arguments); |
|
|
|
if(this.href){ |
|
if(!this._xhrDfd && // if there's an href that isn't already being loaded |
|
(!this.isLoaded || this._hrefChanged || this.refreshOnShow) |
|
){ |
|
return this.refresh(); // If child has an href, promise that fires when the load is complete |
|
} |
|
} |
|
}, |
|
|
|
refresh: function(){ |
|
// summary: |
|
// [Re]download contents of href and display |
|
// description: |
|
// 1. cancels any currently in-flight requests |
|
// 2. posts "loading..." message |
|
// 3. sends XHR to download new data |
|
|
|
// Cancel possible prior in-flight request |
|
this.cancel(); |
|
|
|
this.onLoadDeferred = new Deferred(lang.hitch(this, "cancel")); |
|
this.onLoadDeferred.then(lang.hitch(this, "onLoad")); |
|
this._load(); |
|
return this.onLoadDeferred; // If child has an href, promise that fires when refresh is complete |
|
}, |
|
|
|
_load: function(){ |
|
// summary: |
|
// Load/reload the href specified in this.href |
|
|
|
// display loading message |
|
this._setContent(this.onDownloadStart(), true); |
|
|
|
var self = this; |
|
var getArgs = { |
|
preventCache: (this.preventCache || this.refreshOnShow), |
|
url: this.href, |
|
handleAs: "text" |
|
}; |
|
if(lang.isObject(this.ioArgs)){ |
|
lang.mixin(getArgs, this.ioArgs); |
|
} |
|
|
|
var hand = (this._xhrDfd = (this.ioMethod || xhr.get)(getArgs)), |
|
returnedHtml; |
|
|
|
hand.then( |
|
function(html){ |
|
returnedHtml = html; |
|
try{ |
|
self._isDownloaded = true; |
|
return self._setContent(html, false); |
|
}catch(err){ |
|
self._onError('Content', err); // onContentError |
|
} |
|
}, |
|
function(err){ |
|
if(!hand.canceled){ |
|
// show error message in the pane |
|
self._onError('Download', err); // onDownloadError |
|
} |
|
delete self._xhrDfd; |
|
return err; |
|
} |
|
).then(function(){ |
|
self.onDownloadEnd(); |
|
delete self._xhrDfd; |
|
return returnedHtml; |
|
}); |
|
|
|
// Remove flag saying that a load is needed |
|
delete this._hrefChanged; |
|
}, |
|
|
|
_onLoadHandler: function(data){ |
|
// summary: |
|
// This is called whenever new content is being loaded |
|
this._set("isLoaded", true); |
|
try{ |
|
this.onLoadDeferred.resolve(data); |
|
}catch(e){ |
|
console.error('Error ' + (this.widgetId || this.id) + ' running custom onLoad code: ' + e.message); |
|
} |
|
}, |
|
|
|
_onUnloadHandler: function(){ |
|
// summary: |
|
// This is called whenever the content is being unloaded |
|
this._set("isLoaded", false); |
|
try{ |
|
this.onUnload(); |
|
}catch(e){ |
|
console.error('Error ' + this.widgetId + ' running custom onUnload code: ' + e.message); |
|
} |
|
}, |
|
|
|
destroyDescendants: function(/*Boolean*/ preserveDom){ |
|
// summary: |
|
// Destroy all the widgets inside the ContentPane and empty containerNode |
|
|
|
// Make sure we call onUnload (but only when the ContentPane has real content) |
|
if(this.isLoaded){ |
|
this._onUnloadHandler(); |
|
} |
|
|
|
// Even if this.isLoaded == false there might still be a "Loading..." message |
|
// to erase, so continue... |
|
|
|
// For historical reasons we need to delete all widgets under this.containerNode, |
|
// even ones that the user has created manually. |
|
var setter = this._contentSetter; |
|
array.forEach(this.getChildren(), function(widget){ |
|
if(widget.destroyRecursive){ |
|
// All widgets will hit this branch |
|
widget.destroyRecursive(preserveDom); |
|
}else if(widget.destroy){ |
|
// Things like dojo/dnd/Source have destroy(), not destroyRecursive() |
|
widget.destroy(preserveDom); |
|
} |
|
widget._destroyed = true; |
|
}); |
|
if(setter){ |
|
// Most of the widgets in setter.parseResults have already been destroyed, but |
|
// things like Menu that have been moved to <body> haven't yet |
|
array.forEach(setter.parseResults, function(widget){ |
|
if(!widget._destroyed){ |
|
if(widget.destroyRecursive){ |
|
// All widgets will hit this branch |
|
widget.destroyRecursive(preserveDom); |
|
}else if(widget.destroy){ |
|
// Things like dojo/dnd/Source have destroy(), not destroyRecursive() |
|
widget.destroy(preserveDom); |
|
} |
|
widget._destroyed = true; |
|
} |
|
}); |
|
delete setter.parseResults; |
|
} |
|
|
|
// And then clear away all the DOM nodes |
|
if(!preserveDom){ |
|
domConstruct.empty(this.containerNode); |
|
} |
|
|
|
// Delete any state information we have about current contents |
|
delete this._singleChild; |
|
}, |
|
|
|
_setContent: function(/*String|DocumentFragment*/ cont, /*Boolean*/ isFakeContent){ |
|
// summary: |
|
// Insert the content into the container node |
|
// returns: |
|
// Returns a Deferred promise that is resolved when the content is parsed. |
|
|
|
cont = this.preprocessContent(cont); |
|
// first get rid of child widgets |
|
this.destroyDescendants(); |
|
|
|
// html.set will take care of the rest of the details |
|
// we provide an override for the error handling to ensure the widget gets the errors |
|
// configure the setter instance with only the relevant widget instance properties |
|
// NOTE: unless we hook into attr, or provide property setters for each property, |
|
// we need to re-configure the ContentSetter with each use |
|
var setter = this._contentSetter; |
|
if(!(setter && setter instanceof html._ContentSetter)){ |
|
setter = this._contentSetter = new html._ContentSetter({ |
|
node: this.containerNode, |
|
_onError: lang.hitch(this, this._onError), |
|
onContentError: lang.hitch(this, function(e){ |
|
// fires if a domfault occurs when we are appending this.errorMessage |
|
// like for instance if domNode is a UL and we try append a DIV |
|
var errMess = this.onContentError(e); |
|
try{ |
|
this.containerNode.innerHTML = errMess; |
|
}catch(e){ |
|
console.error('Fatal ' + this.id + ' could not change content due to ' + e.message, e); |
|
} |
|
})/*, |
|
_onError */ |
|
}); |
|
} |
|
|
|
var setterParams = lang.mixin({ |
|
cleanContent: this.cleanContent, |
|
extractContent: this.extractContent, |
|
parseContent: !cont.domNode && this.parseOnLoad, |
|
parserScope: this.parserScope, |
|
startup: false, |
|
dir: this.dir, |
|
lang: this.lang, |
|
textDir: this.textDir |
|
}, this._contentSetterParams || {}); |
|
|
|
var p = setter.set((lang.isObject(cont) && cont.domNode) ? cont.domNode : cont, setterParams); |
|
|
|
// dojox/layout/html/_base::_ContentSetter.set() returns a Promise that indicates when everything is completed. |
|
// dojo/html::_ContentSetter.set() currently returns the DOMNode, but that will be changed for 2.0. |
|
// So, if set() returns a promise then use it, otherwise fallback to waiting on setter.parseDeferred |
|
var self = this; |
|
return when(p && p.then ? p : setter.parseDeferred, function(){ |
|
// setter params must be pulled afresh from the ContentPane each time |
|
delete self._contentSetterParams; |
|
|
|
if(!isFakeContent){ |
|
if(self._started){ |
|
// Startup each top level child widget (and they will start their children, recursively) |
|
self._startChildren(); |
|
|
|
// Call resize() on each of my child layout widgets, |
|
// or resize() on my single child layout widget... |
|
// either now (if I'm currently visible) or when I become visible |
|
self._scheduleLayout(); |
|
} |
|
self._onLoadHandler(cont); |
|
} |
|
}); |
|
}, |
|
|
|
preprocessContent: function(/*String|DocumentFragment*/ content){ |
|
// summary: |
|
// Hook, called after content has loaded, before being processed. |
|
// description: |
|
// A subclass should preprocess the content and return the preprocessed content. |
|
// See https://bugs.dojotoolkit.org/ticket/9622 |
|
// returns: |
|
// Returns preprocessed content, either a String or DocumentFragment |
|
return content; |
|
}, |
|
|
|
_onError: function(type, err, consoleText){ |
|
this.onLoadDeferred.reject(err); |
|
|
|
// shows user the string that is returned by on[type]Error |
|
// override on[type]Error and return your own string to customize |
|
var errText = this['on' + type + 'Error'].call(this, err); |
|
if(consoleText){ |
|
console.error(consoleText, err); |
|
}else if(errText){// a empty string won't change current content |
|
this._setContent(errText, true); |
|
} |
|
}, |
|
|
|
// EVENT's, should be overide-able |
|
onLoad: function(/*===== data =====*/){ |
|
// summary: |
|
// Event hook, is called after everything is loaded and widgetified |
|
// tags: |
|
// callback |
|
}, |
|
|
|
onUnload: function(){ |
|
// summary: |
|
// Event hook, is called before old content is cleared |
|
// tags: |
|
// callback |
|
}, |
|
|
|
onDownloadStart: function(){ |
|
// summary: |
|
// Called before download starts. |
|
// description: |
|
// The string returned by this function will be the html |
|
// that tells the user we are loading something. |
|
// Override with your own function if you want to change text. |
|
// tags: |
|
// extension |
|
return this.loadingMessage; |
|
}, |
|
|
|
onContentError: function(/*Error*/ /*===== error =====*/){ |
|
// summary: |
|
// Called on DOM faults, require faults etc. in content. |
|
// |
|
// In order to display an error message in the pane, return |
|
// the error message from this method, as an HTML string. |
|
// |
|
// By default (if this method is not overriden), it returns |
|
// nothing, so the error message is just printed to the console. |
|
// tags: |
|
// extension |
|
}, |
|
|
|
onDownloadError: function(/*Error*/ /*===== error =====*/){ |
|
// summary: |
|
// Called when download error occurs. |
|
// |
|
// In order to display an error message in the pane, return |
|
// the error message from this method, as an HTML string. |
|
// |
|
// Default behavior (if this method is not overriden) is to display |
|
// the error message inside the pane. |
|
// tags: |
|
// extension |
|
return this.errorMessage; |
|
}, |
|
|
|
onDownloadEnd: function(){ |
|
// summary: |
|
// Called when download is finished. |
|
// tags: |
|
// callback |
|
} |
|
}); |
|
});
|
|
|