durandal
Version:
Durandal is a cross-device, cross-platform client framework written in JavaScript and designed to make Single Page Applications (SPAs) easy to create and maintain. We've used it to build apps for PC, Mac, Linux, iOS and Android...and now it's your turn...
197 lines (186 loc) • 8.12 kB
JavaScript
/**
* Durandal 2.2.0 Copyright (c) 2010-2016 Blue Spire Consulting, Inc. All Rights Reserved.
* Available via the MIT license.
* see: http://durandaljs.com or https://github.com/BlueSpire/Durandal for details.
*/
/**
* The viewEngine module provides information to the viewLocator module which is used to locate the view's source file. The viewEngine also transforms a view id into a view instance.
* @module viewEngine
* @requires system
* @requires jquery
*/
define(['durandal/system', 'jquery'], function (system, $) {
var parseMarkup;
if ($.parseHTML) {
parseMarkup = function (html) {
return $.parseHTML(html);
};
} else {
parseMarkup = function (html) {
return $(html).get();
};
}
/**
* @class ViewEngineModule
* @static
*/
return {
cache:{},
/**
* The file extension that view source files are expected to have.
* @property {string} viewExtension
* @default .html
*/
viewExtension: '.html',
/**
* The name of the RequireJS loader plugin used by the viewLocator to obtain the view source. (Use requirejs to map the plugin's full path).
* @property {string} viewPlugin
* @default text
*/
viewPlugin: 'text',
/**
* Parameters passed to the RequireJS loader plugin used by the viewLocator to obtain the view source.
* @property {string} viewPluginParameters
* @default The empty string by default.
*/
viewPluginParameters: '',
/**
* Determines if the url is a url for a view, according to the view engine.
* @method isViewUrl
* @param {string} url The potential view url.
* @return {boolean} True if the url is a view url, false otherwise.
*/
isViewUrl: function (url) {
return url.indexOf(this.viewExtension, url.length - this.viewExtension.length) !== -1;
},
/**
* Converts a view url into a view id.
* @method convertViewUrlToViewId
* @param {string} url The url to convert.
* @return {string} The view id.
*/
convertViewUrlToViewId: function (url) {
return url.substring(0, url.length - this.viewExtension.length);
},
/**
* Converts a view id into a full RequireJS path.
* @method convertViewIdToRequirePath
* @param {string} viewId The view id to convert.
* @return {string} The require path.
*/
convertViewIdToRequirePath: function (viewId) {
var plugin = this.viewPlugin ? this.viewPlugin + '!' : '';
return plugin + viewId + this.viewExtension + this.viewPluginParameters;
},
/**
* Parses the view engine recognized markup and returns DOM elements.
* @method parseMarkup
* @param {string} markup The markup to parse.
* @return {DOMElement[]} The elements.
*/
parseMarkup: parseMarkup,
/**
* Calls `parseMarkup` and then pipes the results through `ensureSingleElement`.
* @method processMarkup
* @param {string} markup The markup to process.
* @return {DOMElement} The view.
*/
processMarkup: function (markup) {
var allElements = this.parseMarkup(markup);
return this.ensureSingleElement(allElements);
},
/**
* Converts an array of elements into a single element. White space and comments are removed. If a single element does not remain, then the elements are wrapped.
* @method ensureSingleElement
* @param {DOMElement[]} allElements The elements.
* @return {DOMElement} A single element.
*/
ensureSingleElement:function(allElements){
if (!allElements) {
$('<div></div>')[0];
} else if (allElements.length == 1) {
return allElements[0];
}
var withoutCommentsOrEmptyText = [];
for (var i = 0; i < allElements.length; i++) {
var current = allElements[i];
if (current.nodeType != 8) {
if (current.nodeType == 3) {
var result = /\S/.test(current.nodeValue);
if (!result) {
continue;
}
}
withoutCommentsOrEmptyText.push(current);
}
}
if (withoutCommentsOrEmptyText.length > 1) {
return $(withoutCommentsOrEmptyText).wrapAll('<div class="durandal-wrapper"></div>').parent().get(0);
}
return withoutCommentsOrEmptyText[0];
},
/**
* Gets the view associated with the id from the cache of parsed views.
* @method tryGetViewFromCache
* @param {string} id The view id to lookup in the cache.
* @return {DOMElement|null} The cached view or null if it's not in the cache.
*/
tryGetViewFromCache:function(id) {
return this.cache[id];
},
/**
* Puts the view associated with the id into the cache of parsed views.
* @method putViewInCache
* @param {string} id The view id whose view should be cached.
* @param {DOMElement} view The view to cache.
*/
putViewInCache: function (id, view) {
this.cache[id] = view;
},
/**
* Creates the view associated with the view id.
* @method createView
* @param {string} viewId The view id whose view should be created.
* @return {Promise} A promise of the view.
*/
createView: function(viewId) {
var that = this;
var requirePath = this.convertViewIdToRequirePath(viewId);
var existing = this.tryGetViewFromCache(requirePath);
if (existing) {
return system.defer(function(dfd) {
dfd.resolve(existing.cloneNode(true));
}).promise();
}
return system.defer(function(dfd) {
system.acquire(requirePath).then(function(markup) {
var element = that.processMarkup(markup);
element.setAttribute('data-view', viewId);
that.putViewInCache(requirePath, element);
dfd.resolve(element.cloneNode(true));
}).fail(function(err) {
that.createFallbackView(viewId, requirePath, err).then(function(element) {
element.setAttribute('data-view', viewId);
that.cache[requirePath] = element;
dfd.resolve(element.cloneNode(true));
});
});
}).promise();
},
/**
* Called when a view cannot be found to provide the opportunity to locate or generate a fallback view. Mainly used to ease development.
* @method createFallbackView
* @param {string} viewId The view id whose view should be created.
* @param {string} requirePath The require path that was attempted.
* @param {Error} requirePath The error that was returned from the attempt to locate the default view.
* @return {Promise} A promise for the fallback view.
*/
createFallbackView: function (viewId, requirePath, err) {
var that = this,
message = 'View Not Found. Searched for "' + viewId + '" via path "' + requirePath + '".';
return system.defer(function(dfd) {
dfd.resolve(that.processMarkup('<div class="durandal-view-404">' + message + '</div>'));
}).promise();
}
};
});