(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o * Note 1) If ctx.parent is absent from the component definition and from this method call, the brite * will not append the returned element to the DOM. So, if ctx.parent is null, then the create() must * take care of adding the elements to the DOM. However, the postDisplay will still be called. * * config.animation (experimental) {String} the animation ("fromLeft" , "fromRight", or null) (default undefined) * * config.replace (experimental) {String|jQuery} jquery selector string, html element, or jquery object (default undefined) of the * element to be replaced * * config.emptyParent {Boolean} (default false) if set/true will call empty() on the parent before adding the new element (default * false). Valid only if no transition and build return an element * * config.unique (experimental) {Boolean} if true, the component will be display only if there is not already one component with * the same name in the page. * * config.loadTmpl {Boolean|String} (default false) If true, then, it will load the template the first time this component is displayed. * If it is a string it use it as the file name to be loaded from the directory. If it starts with "/" then, it will be from the base, otherwise, * it will be relative to the template folder. The default template folder is "template/" but can be set by brite.config.tmplPath. * * * config.checkTmpl {Boolean|String|jQuery} (default false). (require config.loadTmpl) If true, it will check if the template for this component has been added, by default it will check "#tmpl-ComponentName". * If it is a string or jQuery, then it will be use with jQuery if it exists. * Note that the check happen only the first time, then, brite will remember for subsequent brite.display * * @param {Object|Function} * componentFactory (Required) Factory function or "object template" that will be used to create the * object instance. If componentFactory is a plain object, the "object template" will be cloned to create * the component instance. If it is a function, it will be called and a component instance object will be * exptected as return value.
*
* * A "Component" object can have the following methods
*
* component.create(data,config): (required) function that will be called with (data,config) to build the * component.$element.
* component.init(data,config): (optional) Will be called just after the create and the component instance has been * initialized.
* component.postDisplay(data,config): (optional) This method will get called with (data,config) after the component * has been created and initialized (postDisplay is deferred for performance optimization)
* Since this call will be deferred, it is a good place to do non-visible logic, such as event bindings.
* component.destroy() (optional) This will get called when $.bRemove or $.bEmpty is called on a parent (or on the * element for $.bRemove). It will get called before this component htmlElement will get removed
* component.postDestroy() (optional) This will get called when $.bRemove or $.bEmpty is called on a parent (or on * the element for $.bRemove). It will get called after this component htmlElement will get removed
* */ core.registerView = function(name, arg1, arg2) { var def = {}; def.name = name; def.componentFactory = (arg2)?arg2:arg1; var config = (arg2)?arg1:null; // no config if only two arguments def.config = $.extend({}, core.viewDefaultConfig,config); _componentDefStore[name] = def; // This resolve the deferred if we had a deferred component loading // (old way, where the brite.register is in the template) var deferred = _deferredByComponentName[name]; if (deferred) { deferred.resolve(def); delete _deferredByComponentName[name]; } }; /** * This just instantiate a new component for a given name. This is useful for manipulating the component off * lifecycle for performance. For example, sometime building a component and displaying in the background (with * z-index) allow the browser to do its caching magic, and can speed up the first appearance of the component when * it is due. * * @param {string} * name */ /* DEPRECATED for now brite.instantiateComponent = function(name) { var loaderDeferred = loadComponent(name); return instantiateComponent(componentDef); } */ // ------ /Public API: Component Management ------ // // ------ Public API: Transition Management ------ // // deprecated core.registerTransition = function(name, transition) { _transitions[name] = transition; }; // deprecated core.getTransition = function(name) { return _transitions[name]; }; // ------ /Public API: Transition Management ------ // // ------ Public API: Display Management ------ // /** * This will create, init, and display a new view. It will load the view on demand if needed. * * @param {String} * name (required) the view name * @param {Element} HTML Element, jQuery element, or a jQuery selector, where the element will be added. * @param {Object} * data (optional, required if config) the data to be passed to the build and postDisplay. * @param {Object} * config (optional) config override the component's config (see {@link brite.registerComponent} config * params for description) * @return {Component} return the component instance. */ core.display = function(viewName, parent, data, config) { if (parent){ config = config || {}; config.parent = parent; } return process(viewName, data, config); }; // deprecated core.legacyDisplay = function(viewName, data, config) { var parent = (config)?config.parent:null; core.display(viewName,parent,data,config); }; /** * Same as brite.display but bypass the build() step (postDisplay() will still be called). So, this will create a * new component and attach it to the $element and call postDisplay on it. * */ core.attach = function(viewName, $element, data, config) { return process(viewName, data, config, $element); }; // ------ /Public API: Display Management ------ // // ------ Public Properties: Config ------ // /** * Config for the brite module. * * */ core.config = { componentsHTMLHolder: "body", tmplPath: "tmpl/", // deprecated jsPath: "js/", // deprecated cssPath: "css/", // deprecated tmplExt: ".tmpl" // deprecated }; core.viewDefaultConfig = { loadTmpl: false, // deprecated loadCss: false, // deprecated emptyParent : false, postDisplayDelay : 0 }; // ------ /Public Properties: Config ------ // /** * Return the promise().
* * *
* TODO: Needs to make the the component * * @param {Object} * name component name (no space or special character) * @return The loaderDeferred */ function loadComponent(name) { var loaderDeferred = $.Deferred(); var loadComponentDefDfd = loadComponentDef(name); loadComponentDefDfd.done(function(componentDef){ var loadTemplateDfd, loadCssDfd; // --------- Load the tmpl if needed --------- // var loadTemplate = componentDef.config.loadTmpl; var url; if (loadTemplate && !_templateLoadedPerComponentName[name] ){ // if we have a check template, we need to check if the template has been already loaded var needsToLoadTemplate = true; var checkTemplate = componentDef.config.checkTemplate; if (checkTemplate){ var templateSelector = (typeof checkTemplate == "string")?checkTemplate:("#tmpl-" + name); if ($(templateSelector).length > 0){ needsToLoadTemplate = false; } } if (needsToLoadTemplate){ loadTemplateDfd = $.Deferred(); // if it is a string, then, it is the templatename, otherwise, the component name is the name var templateName = (typeof loadTemplate == "string")?templateName:(name + ".html"); url = null; if (typeof core.config.tmplPath === "function") { url = core.config.tmplPath(name); }else{ url = core.config.tmplPath + name + core.config.tmplExt; } $.ajax({ url : url, async : true }).complete(function(jqXHR) { $(core.config.componentsHTMLHolder).append(jqXHR.responseText); _templateLoadedPerComponentName[name] = true; loadTemplateDfd.resolve(); }); } } // --------- /Load the tmpl if needed --------- // // --------- Load the css if needed --------- // var loadCss = componentDef.config.loadCss; if (loadCss){ //TODO: need to add the checkCss support loadCssDfd = $.Deferred(); url = null; if (typeof core.config.cssPath === "function") { url = core.config.cssPath(name); }else{ url = core.config.cssPath + name + ".css"; } var includeDfd = includeFile(url,"css"); includeDfd.done(function(){ loadCssDfd.resolve(); }).fail(function(){ if (console){ console.log("Brite ERROR: cannot load " + url + ". Ignoring issue"); } loadCssDfd.resolve(); }); } // --------- /Load the Template if needed --------- // $.when(loadTemplateDfd,loadCssDfd).done(function(){ loaderDeferred.resolve(componentDef); }); }); loadComponentDefDfd.fail(function(ex){ if (console){ console.log("BRITE-ERROR: Brite cannot load component: " + name + "\n\t " + ex); } loaderDeferred.reject(); }); return loaderDeferred.promise(); } // Load the componentDef if needed and return the promise for it function loadComponentDef(name){ var dfd = $.Deferred(); var componentDef = _componentDefStore[name]; if (componentDef){ dfd.resolve(componentDef); }else{ var resourceFile = null; if (typeof core.config.jsPath === "function") resourceFile = core.config.jsPath(name); else resourceFile = core.config.jsPath + name + ".js"; var includeDfd = includeFile(resourceFile,"js"); includeDfd.done(function(){ componentDef = _componentDefStore[name]; if (componentDef){ dfd.resolve(componentDef); }else{ dfd.reject("Component js file [" + resourceFile + "] loaded, but it did not seem to have registered the view - it needs to call brite.registerView('" + name + "',...config...) - see documentation"); } }).fail(function(){ dfd.reject("Component resource file " + resourceFile + " not found"); }); } return dfd.promise(); } // if $element exist, then, bypass the create function process(name, data, config, $element) { var loaderDeferred = loadComponent(name); var processDeferred = $.Deferred(); var createDeferred = $.Deferred(); var initDeferred = $.Deferred(); var postDisplayDeferred = $.Deferred(); var processPromise = processDeferred.promise(); processPromise.whenCreate = createDeferred.promise(); processPromise.whenInit = initDeferred.promise(); processPromise.whenPostDisplay = postDisplayDeferred.promise(); loaderDeferred.done(function(componentDef) { config = buildConfig(componentDef, config); var component = instantiateComponent(componentDef); // If the config.unique is set, and there is a component with the same name, we resolve the deferred now // NOTE: the whenCreate and whenPostDisplay won't be resolved again // TODO: an optimization point would be to add a "bComponentUnique" in the class for data-b-view that // have a confi.unique = true // This way, the query below could be ".bComponentUnique [....]" and should speedup the search significantly // on UserAgents that supports the getElementsByClassName if (config.unique) { var $component = $("[data-b-view='" + name + "']"); if ($component.length > 0) { component = $component.bComponent(); processDeferred.resolve(component); return processDeferred; } } // ------ create ------ // var deferred$element = $.Deferred(); // if there is no element, we invoke the build if (!$element) { // Ask the component to create the new $element var createReturn = invokeCreate(component, data, config); // if it custom Deferred, then, assume it will get resolved with the $element (as by the API contract) if (createReturn && $.isFunction(createReturn.promise) && !createReturn.jquery) { // TODO: will need to use the new jQuery 1.6 pipe here (right now, just trigger on done) createReturn.done(function($element) { deferred$element.resolve($element); }).fail(function() { deferred$element.reject(); }); } // otherwise, if the $element is returned , resolve the deferred$element immediately else { if (createReturn) { $element = createReturn; } deferred$element.resolve($element); } } // if the $element is already here, then, it is an attach, so, do a immediate Deffered else { deferred$element.resolve($element); } // ------ /create ------ // // ------ render & resolve ------ // deferred$element.promise().done(function(createResult) { // if there is an element, then, manage the rendering logic. var $element; if (createResult) { if (typeof createResult === "string"){ createResult = createResult.trim(); } // make sure we get the jQuery object $element = $(createResult); bind$element($element, component, data, config); // attached the componentPromise to this $element, this way, during rendering sub component can sync // with it. $element.data("componentProcessPromise", processPromise); createDeferred.resolve(component); $.when(invokeInit(component, data, config)).done(function() { // render the element // TODO: implement deferred for the render as well. renderComponent(component, data, config); // TODO: this might need to be fore the renderComponent initDeferred.resolve(component); }); } else { // TODO: need to look if we need this. Basically, that allow to have create methods that do/return // nothing but still instantiate the component createDeferred.resolve(component); // TODO: probably need to invokeInit in this case as well. For now, just resolve the initDeferred initDeferred.resolve(component); } processPromise.whenInit.done(function() { var parentComponentProcessPromise, invokePostDisplayDfd; // if there is a parent component, then need to wait until it display to display this one. if ($element && $element.parent()) { var parentComponent$Element = $element.parent().closest("[data-b-view]"); if (parentComponent$Element.length > 0) { parentComponentProcessPromise = parentComponent$Element.data("componentProcessPromise"); if (parentComponentProcessPromise){ parentComponentProcessPromise.whenPostDisplay.done(function() { invokePostDisplayDfd = invokePostDisplay(component, data, config); invokePostDisplayDfd.done(function() { postDisplayDeferred.resolve(component); }).fail(function(err){ postDisplayDeferred.reject(err); }); }); } } } // if we did not have any parentComponentProcessPromise, then, just invoke if (!parentComponentProcessPromise) { invokePostDisplayDfd = invokePostDisplay(component, data, config); invokePostDisplayDfd.done(function() { postDisplayDeferred.resolve(component); }).fail(function(err){ postDisplayDeferred.reject(err); }); } }); }); // ------ /render & resolve ------ // processPromise.whenPostDisplay.done(function() { processDeferred.resolve(component); }).fail(function(err){ processDeferred.reject(err); }); }); loaderDeferred.fail(function(){ processDeferred.reject(); createDeferred.reject(); initDeferred.reject(); postDisplayDeferred.reject(); }); return processPromise; } function renderComponent(component, data, config) { var $parent; if (config.transition) { var transition = core.getTransition(config.transition); if (transition) { transition(component, data, config); } else { console.log("BRITE ERROR Transition [" + config.transition + "] not found. Transitions need to be registered via brite.registerTranstion(..) before call."); } } // if no transition remove/show else { if (config.replace) { $(config.replace).bRemove(); } // note: if there is no parent, then, the sUI.diplay caller is responsible to add it if (config.parent) { $parent = $(config.parent); if ($parent.length > 0){ if (config.emptyParent) { $parent.bEmpty(); } $parent.append(component.$el); }else{ if (console){ console.log("BRITE WARNING - parent ", config.parent, " not found when displaying", component); } } }else { if (console){ console.log("BRITE WARNING - no parent specified ", component, config); } } } } // ------ Helpers ------ // // build a config for a componentDef function buildConfig(componentDef, config) { var instanceConfig = $.extend({}, componentDef.config, config); instanceConfig.componentName = componentDef.name; return instanceConfig; } function instantiateComponent(componentDef) { var component; var componentFactory = componentDef.componentFactory; if (componentFactory) { // if it is a function, call it, it should return a new component object if ($.isFunction(componentFactory)) { component = componentFactory(); } // if it is a plainObject, then, we clone it (NOTE: We do a one level clone) else if ($.isPlainObject(componentFactory)) { component = $.extend({}, componentFactory); } else { console.log("BRITE ERROR - Invalid ComponentFactory for component [" + componentDef.componentName + "]. Only types Function or Object are supported as componentFactory. Empty component will be created."); } } else { console.log("BRITE ERROR - No ComponentFactory for component [" + componentDef.componentName + "]"); } if (component) { component.name = componentDef.name; // .cid is a legacy property, .id is the one to use. component.cid = component.id = "bview_" + cidSeq++; } return component; } function invokeCreate(component, data, config) { // backward compatibility var createFunc = component.create || component.build; // assert that we have a build method if (!createFunc || !$.isFunction(createFunc)) { console.log("BRITE ERROR - Invalid 'create' function for component [" + component.name + "]."); return; } return createFunc.call(component, data, config); } function invokeInit(component, data, config) { var initFunc = component.init; if ($.isFunction(initFunc)) { return initFunc.call(component, data, config); } } // function bind$element($element, component, data, config) { component.el = $element[0]; // component.$element is for deprecated, .$el is te way to access it. component.$el = component.$element = $element; $element.data("bview", component); $element.attr("data-b-view", config.componentName); $element.attr("data-brite-cid", component.cid); } // Note: This will be called even if .postDisplay is not defined (test is inside this method) // So, we do the view events binding here. function invokePostDisplay(component, data, config) { var invokeDfd = $.Deferred(); // bind the view events if (component.events){ bindEvents(component.events,component.$el,component); } // bind the document events (note: need to have a namespace since they will need to be cleaned up) if (component.docEvents){ bindEvents(component.docEvents,$(document),component, utils.DOC_EVENT_NS_PREFIX + component.id); } // bind the window events if present if (component.winEvents){ bindEvents(component.winEvents,$(window),component, utils.WIN_EVENT_NS_PREFIX + component.id); } if (component.parentEvents){ $.each(component.parentEvents,function(key, val){ var parent = component.$el.bView(key); if (parent){ var events = component.parentEvents[key]; bindEvents(events,parent.$el,component,"." + component.id); } }); } bindDaoEvents(component); // Call the eventual postDisplay // (differing for performance) if (component.postDisplay) { // if the component has a delay >= 0, then, we use a setTimeout if (config.postDisplayDelay >= 0) { setTimeout(function() { performPostDisplay(component, data, config, invokeDfd); }, config.postDisplayDelay); } // otherwise, we call it in sync else { performPostDisplay(component, data, config, invokeDfd); } } // if there is now postDisplay, then, trigger it anyway else { invokeDfd.resolve(); } return invokeDfd.promise(); } function performPostDisplay(component, data, config, invokeDfd){ if (!component.$el){ invokeDfd.reject("BRITE ERROR cannot call postDisplay a view already deleted " + ((component)?component.name:"")); return; } var postDisplayDfd = component.postDisplay(data, config); if (postDisplayDfd && $.isFunction(postDisplayDfd.promise)) { postDisplayDfd.done(function() { invokeDfd.resolve(); }); } else { invokeDfd.resolve(); } } function bindEvents(eventMap,$baseElement,component,namespace){ $.each(eventMap,function(edef,etarget){ var edefs = edef.split(";"); // get the event name(s) (space seperated) var ename = edefs[0]; // If we have a namespace, add the namspace to each name if (namespace) { ename = $.map($.trim(ename).split(' '), function(val) { return val + namespace; }).join(' '); } var eselector = edefs[1]; // can be undefined, but in this case it is direct. var efn = getFn(component,etarget); if (efn){ $baseElement.on(ename,eselector,function(){ var args = $.makeArray(arguments); efn.apply(component,args); }); }else{ throw "BRITE ERROR: '" + component.name + "' component event handler function '" + etarget + "' not found."; } }); } function bindDaoEvents(component){ var daoEvents = component.daoEvents; if (component.daoEvents){ // for now, the namespace is just the component id var ns = component.id; $.each(daoEvents,function(edef,etarget){ var efn = getFn(component,etarget); if (efn){ var edefs = edef.split(";"); var ename = edefs[0]; ename = ename.charAt(0).toUpperCase() + ename.slice(1); var eventTypes = edefs[1]; var entityTypes = edefs[2]; brite.dao["on" + ename](eventTypes,entityTypes,function(){ var args = $.makeArray(arguments); efn.apply(component,args); },ns); }else{ throw "BRITE ERROR: '" + component.name + "' component daoEvent handler function '" + etarget + "' not found."; } }); } } function getFn(component,target){ var fn = target; if (!$.isFunction(fn)){ fn = component[target]; } return fn; } // ------ /Helpers ------ // // --------- File Include (JS & CSS) ------ // /* * Include the file name in the part of the DOM and return a deferred that will resolve when done */ function includeFile(fileName, fileType) { var dfd = $.Deferred(); var fileref; if(fileType === "js") { fileref = document.createElement('script'); fileref.setAttribute("type", "text/javascript"); fileref.setAttribute("src", fileName); } else if(fileType === "css") { fileref = document.createElement("link"); fileref.setAttribute("rel", "stylesheet"); fileref.setAttribute("type", "text/css"); fileref.setAttribute("href", fileName); } if (fileType === "js"){ if (fileref.addEventListener){ fileref.onload = function(){ dfd.resolve(fileName); }; }else{ // for old IE // TODO: probably need to handle the error case here fileref.onreadystatechange = function(){ if (fileref.readyState === "loaded" || fileref.readyState === "complete"){ dfd.resolve(fileName); } }; } if (fileref.addEventListener){ fileref.addEventListener('error', function(){ dfd.reject(); }, true); } }else if (fileType === "css"){ if (document.all){ // The IE way, which is interestingly the most standard fileref.onreadystatechange = function() { var state = fileref.readyState; if (state === 'loaded' || state === 'complete') { fileref.onreadystatechange = null; dfd.resolve(fileName); } }; }else{ // unfortunately, this will rarely be taken in account in modern browsers if (fileref.addEventListener) { fileref.addEventListener('load', function() { dfd.resolve(fileName); }, false); } // hack from: http://www.backalleycoder.com/2011/03/20/link-tag-css-stylesheet-load-event/ var html = document.getElementsByTagName('html')[0]; var img = document.createElement('img'); $(img).css("display","none"); // hide the image img.onerror = function(){ html.removeChild(img); // for css, we cannot know if it fail to load for now dfd.resolve(fileName); }; html.appendChild(img); img.src = fileName; } } if( typeof fileref != "undefined") { document.getElementsByTagName("head")[0].appendChild(fileref); } return dfd.promise(); } // --------- /File Include (JS & CSS) ------ // },{"./brite-utils.js":6}],2:[function(require,module,exports){ 'use strict'; // Only hard dependency var $ = jQuery; var daoDic = {}; //data change listeners //var daoChangeEventListeners = {}; //daoListeners //var daoListeners = {}; function getDao(objectType) { var dao = daoDic[objectType]; if (dao) { return dao; } else { var er = "Cannot find the DAO for objectType: " + objectType; throw er; } } var dao = getDao; module.exports = { dao: dao, registerDao: registerDao, triggerDataChange: triggerDataChange }; /** * @namespace brite.dao data manager layers to register, access DAOs. * DAOs are javascript objects that must implement the following CRUD methods get, list, create, update, remove methods.
* Signatures of these methods should match the corresponding brite.dao.** methods.
*
* Note that DAO CRUD methods can return directly the result or a deferred object. Also, it is important to note that brite.dao.*** CRUD access methods * will always return deferred object (either the DAO return deferred, or a wrapped deferred if the DAO method did not return a deferred)
*
* The deferred pattern for daos allows the application to be agnostic about the call mode, synchronous or asynchronous (e.g. Ajax, Workers, and other callback based called), * and consequently offer the maximum flexibility during development and production. It also enforce a good practice on how to build the UI components.
*
* If there is a need to access the daos result directly, the brite.sdm ("straight dm") can be used. */ // --------- DAO Support --------- // var internalMethods = { isDataChange : true, entityType: true }; var dataChangeMethodRegEx = /remove|delete|create|update/i; /** * Register a DAO for a given object type. A DAO must implements the "CRUD" method, get, list, create, update, remove and must return (directly * or via deferred) the appropriate result value. * * @param {DAO Oject} a Dao instance that implement the crud methods: get, find, create, update, remove. */ function registerDao(daoHandler) { var daoObject = {}; // support function or property var entityType = ($.isFunction(daoHandler.entityType))?daoHandler.entityType():daoHandler.entityType; if (!entityType || typeof entityType !== "string"){ throw "Cannot register daoHandler because entityType '" + entityType + "' is not valid." + " Make sure the daoHandler emplement .entityType() method which must return a string of the entity type"; } daoObject._entityType = entityType; daoObject._handler = daoHandler; $.each(daoHandler, function(k) { // if it is a function and not an internalMethods if ($.isFunction(daoHandler[k]) && !internalMethods[k]) { var methodName = k; var isDataChange = dataChangeMethodRegEx.test(methodName); if (daoHandler.isDataChange){ isDataChange = isDataChange || daoHandler.isDataChange(methodName); } daoObject[methodName] = (function(entityType, methodName, isDataChange) { return function() { var resultObj = daoHandler[methodName].apply(daoHandler, arguments); var resultPromise = wrapWithDeferred(resultObj); _triggerOnDao(entityType, methodName, resultPromise); resultPromise.done(function(result) { _triggerOnResult(entityType, methodName, result); if (isDataChange) { brite.triggerDataChange(entityType, methodName, result); } }); return resultPromise; }; })(entityType, methodName, isDataChange); } }); daoDic[entityType] = daoObject; if ($.isFunction(daoObject.init)){ daoObject.init(entityType); } return daoObject; } // --------- Internal Utilities For Dao Events --------- // var _ALL_ = "_ALL_"; /** * Build the arguments for all the brite.dao.on*** events from the arguments * Can be * - (entityTypes,actions,func,namespace) * - (entityTypes,func,namespace) * - (func,namespace) * * Return an object with * .events (with the namespace) * .objectTypes (as class css selector, ".User, .Task" * .func the function to register * .namespace */ function buildDaoOnEventParamMap(args) { var i, val, namespace, map = {}; // build the map for ( i = args.length - 1; i > -1; i--) { val = args[i]; // if it is a function, set it. if ($.isFunction(val)) { map.func = val; } // if we did not get the function yet, this is the name space else if (!map.func) { namespace = val; } // if we have the func, and it is the second argument, it si the actions else if (map.func && i === 1) { map.actions = val; } // if we have the func, and it is the first argument, it is objectTypes else if (map.func && i === 0) { map.objectTypes = val; } } // create the namespace if not present if ( typeof namespace === "undefined") { throw "BRITE DAO BINDING ERROR: any binding with brite.dao.on*** needs to have a namespace after the function. " + " Remember to cleanup the event at component close with brite.dao.off(mynamespace)"; } // complete the actions if (!map.actions) { map.actions = _ALL_ + "." + namespace; } else { var ns = "." + namespace + " "; // build the events, split by ',', add the namespace, and join back map.actions = map.actions.split(",").join(ns) + ns; } // complete the objectTypes // build the objectTypes, split by ',', add the "." prefix, and join back if (map.objectTypes) { var objectTypes = map.objectTypes.split(","); $.each(objectTypes, function(idx, val) { objectTypes[idx] = "." + $.trim(val); }); map.objectTypes = objectTypes.join(","); } map.namespace = namespace; return map; } /** * Utility method that will construct a jQuery event with the daoEvent * and trigger it to the appropriate $receiver given the dictionary and objectType * */ function _triggerDaoEvent(dic, $receiversRoot, objectType, daoEvent) { var evt = $.extend(jQuery.Event(daoEvent.action), { daoEvent : daoEvent }); var $receiver = dic[objectType]; if (!$receiver) { dic[objectType] = $receiver = $("
"); $receiversRoot.append($receiver); } // trigger with the event.type == action $receiver.trigger(evt); // in the case of a "remove" event, we need to check if the $receiver did not get removed, // otherwise, we need to add it back. if(evt.type === "remove" && $receiversRoot.find("." + objectType).size() == 0 && $receiver){ $receiversRoot.append($receiver); } // trigger _ALL_ action in case there are some events registered for all event evt.type = _ALL_; $receiver.trigger(evt); } // --------- /Internal Utilities For Dao Events --------- // // --------- brite.dao.onDao --------- // var $daoDao = $("
"); // dictionary of {objectType:$dataEventReceiver} var onDaoReceiverDic = {}; /** * This will trigger on any DAO calls before the dao action is completed (for * asynch daos), hence, the resultPromise property of the daoEvent. * * @param objectTypes e.g., "User, Task" (null for any) * @param actions e.g., "create, list, get" (null for any) * @param listenerFunction The function to be called with the daoEvent * listenerFunction(event) with event.daoEvent as * daoEvent.action * daoEvent.entityType * daoEvent.resultPromise * */ dao.onDao = function(objectTypes, actions, listenerFunction, namespace) { var map = buildDaoOnEventParamMap(arguments); $daoDao.on(map.actions, map.objectTypes, map.func); return map.namespace; }; dao.offDao = function(namespace) { $daoDao.off("." + namespace); }; function _triggerOnDao(entityType, action, resultPromise) { var daoEvent = { entityType : entityType, action : action, resultPromise : resultPromise }; _triggerDaoEvent(onDaoReceiverDic, $daoDao, entityType, daoEvent); } // --------- /brite.dao.onDao --------- // // --------- brite.dao.onResult --------- // var $daoResult = $("
"); // dictionary of {objectType:$dataEventReceiver} var onResultReceiverDic = {}; /** * This will trigger when the dao resolve the result of a particular DAO call. * This will not trigger in case of a dao failure. * * @param objectTypes e.g., "User, Task" (null for any) * @param actions e.g., "create, list, get" * @param listenerFunction The function to be called with the daoEvent * listenerFunction(daoEvent) * daoEvent.action * daoEvent.objectType * daoEvent.objectId * daoEvent.data * daoEvent.opts * daoEvent.result */ dao.onResult = function(objectTypes, actions, listenerFunction, namespace) { var map = buildDaoOnEventParamMap(arguments); $daoResult.on(map.actions, map.objectTypes, map.func); return map.namespace; }; dao.offResult = function(namespace) { $daoResult.off("." + namespace); }; function _triggerOnResult(entityType, action, result) { var daoEvent = { entityType: entityType, action : action, result : result }; _triggerDaoEvent(onResultReceiverDic, $daoResult, entityType, daoEvent); } // --------- /brite.dao.onResult --------- // // --------- Brite.dao.onDataChange --------- // var $daoDataChange = $("
"); // dictionary of {objectType:$dataEventReceiver} var dataChangeReceiverDic = {}; /** * This trigger on data change event only (like "create, update, remove") and not on others. For other binding, * use the brite.dao.onResult which will trigger anytime * * @param {String} namespace: the namespace for this event. * @param {String} objectTypes: the object types e.g., "User, Task" (null for any object type); * @param {String} actions: this dao action names e.g., "create, update" */ dao.onDataChange = function(objectTypes, actions, func, namespace) { var map = buildDaoOnEventParamMap(arguments); $daoDataChange.on(map.actions, map.objectTypes, map.func); return map.namespace; }; dao.offDataChange = function(namespace) { $daoDataChange.off("." + namespace); }; function triggerDataChange(entityType, action, result) { var daoEvent = { entityType : entityType, action : action, result : result }; _triggerDaoEvent(dataChangeReceiverDic, $daoDataChange, entityType, daoEvent); } // --------- /Brite.dao.onDataChange --------- // dao.offAny = function(namespace){ dao.offResult(namespace); dao.offDao(namespace); dao.offDataChange(namespace); }; /** * Wrap with a deferred object if the obj is not a deferred itself. */ function wrapWithDeferred(obj) { //if it is a deferred, then, trust it, return it. if (obj && $.isFunction(obj.promise)) { return obj; } else { var dfd = $.Deferred(); dfd.resolve(obj); return dfd; } } // --------- /DAO Support --------- // },{}],3:[function(require,module,exports){ 'use strict'; var ua = require("./brite-ua.js"); var utils = require("./brite-utils.js"); var event = {}; // only hard dependency var $ = jQuery; module.exports = event; // ------ brite event helpers ------ // /** * if it is a touch device, populate the event.pageX and event.page& from the event.touches[0].pageX/Y * @param {jQuery Event} e the jquery event object */ event.fixTouchEvent = function(e){ if (ua.hasTouch()) { var oe = e.originalEvent; if (oe.touches.length > 0) { e.pageX = oe.touches[0].pageX; e.pageY = oe.touches[0].pageY; } } return e; }; /** * Return the event {pageX,pageY} object for a jquery event object (will take the touches[0] if it is a touch event) * @param {jQuery Event} e the jquery event object */ event.eventPagePosition = function(e){ var pageX, pageY; if (e.originalEvent && e.originalEvent.touches){ pageX = e.originalEvent.touches[0].pageX; pageY = e.originalEvent.touches[0].pageY; }else{ pageX = e.pageX; pageY = e.pageY; } return { pageX: pageX, pageY: pageY }; }; // ------ /brite event helpers ------ // // ------ transition helper ------ // /** * simple and convenient methods to perform css3 animations (takes care of the css prefix) * opts.transition: this will be the transition value added as css style (e.g.,: "all 0.3s ease;") * opts.transform: the css transform instruction (e.g.,: "scale(.01)") * opts.onTimeout: (optional, default false). If true or >= 0, then the transformation will be performed on timeout) */ $.fn.bTransition = function(opts) { return this.each(function() { var $this = $(this); var timeout = -1; if (typeof opts.onTimeout === "boolean"){ timeout = (opts.onTimeout)?0:-1; }else if (typeof opts.onTimeout === "number"){ timeout = opts.onTimeout; } if (timeout > -1){ setTimeout(function(){ performTransition($this,opts); },timeout); }else{ performTransition($this,opts); } // add the transition }); }; // helper function function performTransition($this,opts){ $this.css("transition",opts.transition); $this.css("transform",opts.transform); } // ------ /transition helper ------ // // ------ /brite special events ------ // // to prevent other events (i.e., btap) to trigger when dragging. var _dragging = false; var mouseEvents = { start: "mousedown", move: "mousemove", end: "mouseup" }; var touchEvents = { start: "touchstart", move: "touchmove", end: "touchend" }; function getTapEvents(){ if (ua.hasTouch()){ return touchEvents; }else{ return mouseEvents; } } // --------- btap & btaphold --------- // $.event.special.btap = { add: btabAddHandler }; $.event.special.btaphold = { add: btabAddHandler }; function btabAddHandler(handleObj) { var tapEvents = getTapEvents(); $(this).on(tapEvents.start, handleObj.selector, function(evt) { var elem = this; var $elem = $(elem); var origTarget = evt.target, startEvent = evt, timer; function handleEnd(evt){ clearAll(); if (evt.target === origTarget && !_dragging){ // use event.eventPhase because we should ignore bubbling event when triggering this meta event var ep = evt.eventPhase; var pass = (elem === origTarget && ep === 2) || (elem !== origTarget && ep === 3); if (pass && !evt.originalEvent.b_processed){ // we take the pageX and pageY of the start event (because in touch, touchend does not have pageX and pageY) event.fixTouchEvent(startEvent); triggerCustomEvent(elem, evt,{type:"btap",pageX: startEvent.pageX,pageY: startEvent.pageY}); // flag this originalEvent as processed // Note: this allow to prevent multiple triggering without having to use the stopPropagation which will be too // destructive for other event handlers evt.originalEvent.b_processed = true; } } } function clearAll(){ clearTimeout(timer); $elem.off(tapEvents.end,handleEnd); } $elem.on(tapEvents.end,handleEnd); timer = setTimeout(function() { if (!_dragging){ event.fixTouchEvent(startEvent); triggerCustomEvent( elem, startEvent,{type:"btaphold"}); } }, 750 ); }); } linkSpecialEventsTo(["btaphold"],"btap"); // --------- /btap & btaphold --------- // // --------- bdrag* --------- // var BDRAGSTART="bdragstart",BDRAGMOVE="bdragmove",BDRAGEND="bdragend"; // Note: those below are part of the drop events, but are not supported yet. // Need to think some more. // var BDRAGENTER="bdragenter",BDRAGOVER="bdragover",BDRAGLEAVE="bdragleave",BDROP="bdrop"; var dragThreshold = 5; $.event.special[BDRAGSTART] = { add : bdragAddHandler }; $.event.special[BDRAGMOVE] = { add : bdragAddHandler }; $.event.special[BDRAGEND] = { add : bdragAddHandler }; function bdragAddHandler(handleObj) { var tapEvents = getTapEvents(); $(this).on(tapEvents.start, handleObj.selector, function(evt) { var dragStarted = false; var startEvent = evt; var startPagePos = event.eventPagePosition(startEvent); var origTarget = evt.target; var $origTarget = $(origTarget); var $document = $(document); var uid = "_" + utils.uuid(7); // drag move (and start) $document.on(tapEvents.move + "." + uid,function(evt){ var bextra; var currentPagePos = event.eventPagePosition(evt); // fix a bug on Chrome that always change the cursor to text $("body").css("-webkit-user-select","none"); if (!dragStarted){ if(Math.abs(startPagePos.pageX - currentPagePos.pageX) > dragThreshold || Math.abs(startPagePos.pageY - currentPagePos.pageY) > dragThreshold) { dragStarted = true; _dragging = true; $origTarget.data("bDragCtx", {}); bextra = buildDragExtra(evt, $origTarget, BDRAGSTART); triggerCustomEvent( origTarget, evt,{type:BDRAGSTART,target:origTarget,bextra:bextra}); evt.stopPropagation(); evt.preventDefault(); } } if(dragStarted) { bextra = buildDragExtra(evt, $origTarget, BDRAGMOVE); triggerCustomEvent( origTarget, evt,{type:BDRAGMOVE,target:origTarget,bextra:bextra}); evt.stopPropagation(); evt.preventDefault(); } }); // drag end $document.on(tapEvents.end + "." + uid, function(event){ // chrome fix cleanup (remove the hack) $("body").css("-webkit-user-select",""); if (dragStarted){ var bextra = buildDragExtra(evt, $origTarget, BDRAGEND); triggerCustomEvent( origTarget, evt,{type:BDRAGEND,target:origTarget,bextra:bextra}); evt.stopPropagation(); evt.preventDefault(); } $document.off("." + uid); _dragging = false; }); }); } /** * Build the extra event info for the drag event. */ function buildDragExtra(evt,$elem,dragType){ event.fixTouchEvent(evt); var hasTouch = ua.hasTouch(); var extra = { eventSource: evt, pageX: evt.pageX, pageY: evt.pageY }; var oe = evt.originalEvent; if (hasTouch){ extra.touches = oe.touches; } var bDragCtx = $elem.data("bDragCtx"); if (dragType === BDRAGSTART){ bDragCtx.startPageX = extra.startPageX = extra.pageX; bDragCtx.startPageY = extra.startPageY = extra.pageY; bDragCtx.lastPageX = bDragCtx.startPageX = extra.startPageX; bDragCtx.lastPageY = bDragCtx.startPageY = extra.startPageY; }else if (dragType === BDRAGEND){ // because, on iOs, the touchEnd event does not have the .touches[0].pageX extra.pageX = bDragCtx.lastPageX; extra.pageY = bDragCtx.lastPageY; } extra.startPageX = bDragCtx.startPageX; extra.startPageY = bDragCtx.startPageY; extra.deltaX = extra.pageX - bDragCtx.lastPageX; extra.deltaY = extra.pageY - bDragCtx.lastPageY; bDragCtx.lastPageX = extra.pageX; bDragCtx.lastPageY = extra.pageY; return extra; } // --------- /bdrag* --------- // // --------- btransitionend --------- // // Note: even if jQuery 1.8 add the prefix, it still does not normalize the transitionend event. $.event.special.btransitionend = { setup : function(data, namespaces) { var eventListener = "transitionend"; if (this.addEventListener){ if (!ua.browser.mozilla){ eventListener = ua.cssVarPrefix().toLowerCase() + "TransitionEnd"; } this.addEventListener(eventListener,function(evt){ triggerCustomEvent(this,evt,{type:"btransitionend"}); }); }else{ // old browser, just trigger the event since transition should not be supported anyway triggerCustomEvent(this,jQuery.Event("btransitionend"),{type:"btransitionend"}); } } }; // --------- /btransitionend --------- // // --------- Event Utilities --------- // // Link function linkSpecialEventsTo(eventNames,eventRef){ $.each(eventNames,function(idx,val){ $.event.special[ val ] = { setup: function() { $( this ).bind( eventRef, $.noop ); } }; }); } function triggerCustomEvent( elem, nativeEvent, override ) { var newEvent = jQuery.extend( new jQuery.Event(), nativeEvent,override ); $(elem).trigger(newEvent); } // --------- /Event Utilities --------- // // ------ /brite special events ------ // },{"./brite-ua.js":5,"./brite-utils.js":6}],4:[function(require,module,exports){ var dao = require("./brite-dao.js").dao; var utils = require("./brite-utils.js"); module.exports = {}; var $ = jQuery; function processDestroy(component) { // The if(component) is a safeguard in case destroy gets call twice (issue when clicking fast on // test_brite-02-transition....) if (component) { // unbind view events $(document).off(utils.DOC_EVENT_NS_PREFIX + component.id); $(window).off(utils.WIN_EVENT_NS_PREFIX + component.id); dao.offAny(component.id); if (component.parentEvents && component.$el){ $.each(component.parentEvents,function(key, val){ var parent = component.$el.bView(key); if (parent && parent.$el){ parent.$el.off("." + component.id); } }); } var destroyFunc = component.destroy; if ($.isFunction(destroyFunc)) { destroyFunc.call(component); } // Delete this element, as a sign at this component has been destroyed. delete component.$el; delete component.$element; } } // ---------------------------------------------------- // // ------------- brite view jquery plugins ------------ // /** * Safely empty a HTMLElement of its children HTMLElement and bComponent by calling the preRemove and postRemove on * every child components. * * @return the jQuery object */ $.fn.bEmpty = function() { return this.each(function() { var $this = $(this); var componentChildren = $this.bFindComponents(); // call the preRemoves $.each(componentChildren, function(idx, childComponent) { processDestroy(childComponent); }); // do the empty $this.empty(); }); }; /** * Safely remove a HTMLElement and the related bComponent by calling the preRemote and postRemove on every child * components as well as this component. * * @return what a jquery.remove would return */ $.fn.bRemove = function() { return this.each(function() { var $this = $(this); $this.bEmpty(); if ($this.is("[data-b-view]")) { var component = $this.data("bview"); processDestroy(component); $this.remove(); } else { $this.remove(); } }); }; /** * * Return the component that this html element belong to. Thi traverse the tree backwards (this html element up to * document) to find the closest html element containing the brite component for this name. * * If a componentName is given then it will try to find the given component. * * If no componentName is given, then it will return the first component found. * * For example: * * @example var myComponent = $(thisDiv).bComponent("myComponent"); * * @param {String} * componentName The component name to be match when traversing the tree, if undefined, then, the * closestComponent will be return. * */ $.fn.bView = function(viewName) { // iterate and process each matched element var $el; if (viewName) { $el = $(this).closest("[data-b-view='" + viewName + "']"); } else { $el = $(this).closest("[data-b-view]"); } return $el.data("bview"); }; // ------------- /brite view jquery plugins ----------- // // ---------------------------------------------------- // // --------- Entity Plugin --------- // /** * Return the bEntity {id,type,name,$element} (or a list of such) of the closest html element matching entity type in the data-entity. * * The return value is like: * * .type will be the value of the attribute data-entity * .id will be the value of the data-entity-id * .name (optional) will be the value of the data-entity-name * .$el will be the $element containing the matching data-entity attribute * * If no entityType, then, return the first entity of the closest html element having a data-b-entity.
* * $element.bEntity("User"); // return the closest entity with data-entity="User" * $element.bEntity(">children","Task"); // return all the data-entity="Task" children from this $element. * $element.bEntity(">first","Task"); // return the first child entity matching data-entity="Task" * * TODO: needs to implement the >children and >first * * @param {String} entity type (optional) the object * @return null if not found, the first found entity with {id,type,name,$element}. */ $.fn.bEntity = function(entityType) { var result = null; // iterate and process each matched element this.each(function() { // ignore if we already found one if (result === null){ var $this = $(this); var $sObj; if (entityType) { $sObj = $this.closest("[data-entity='" + entityType + "']"); } else { $sObj = $this.closest("[data-entity]"); } if ($sObj.length > 0) { result = { type : $sObj.attr("data-entity"), id : $sObj.attr("data-entity-id"), $el : $sObj }; var name = $sObj.attr("data-entity-name"); if (typeof name !== "undefined"){ result.name = name; } } } }); return result; }; // --------- /Entity Plugin --------- // // --------- old bComponent APIs ------- // // backwards compatibility; $.fn.bComponent = $.fn.bView; /** * Get the list of components that this htmlElement contains. * * @param {string} * componentName (optional) if present, will filter only the component with this matching name * @return a javascript array of all the match component */ // deprecated $.fn.bFindComponents = function(componentName) { var childrenComponents = []; this.each(function() { var $this = $(this); var $componentElements; if (componentName) { $componentElements = $this.find("[data-b-view='" + componentName + "']"); } else { $componentElements = $this.find("[data-b-view]"); } $componentElements.each(function() { var $component = $(this); childrenComponents.push($component.data("bview")); }); }); return childrenComponents; }; /** * Get the list of components that this htmlElement contains. * * @param {string} * componentName (optional) if present, will filter only the component with this matching name * @return a javascript array of all the match component */ // deprecated $.fn.bFindFirstComponent = function(componentName) { var childrenComponents = []; this.each(function() { var $this = $(this); var $componentElements; if (componentName) { $componentElements = $this.find("[data-b-view='" + componentName + "']:first"); } else { $componentElements = $this.find("[data-b-view]:first"); } $componentElements.each(function() { var $component = $(this); childrenComponents.push($component.data("bview")); }); }); return childrenComponents; }; // -------- /old bComponent APIs ------- // // --------- old objRef code --------- // /** * Return the objRef {id,type,$element} (or a list of such) of the closest html element matching the objType match the data-obj_type.
* If no objType, then, return the first objRef of the closest html element having a data-obj_type.
* * @param {String} objType (optional) the object table * @return null if not found, single object with {id,type,$element} if only one jQuery object, a list of such if this jQuery contain multiple elements. */ //@Deprecated $.fn.bObjRef = function(objType) { var resultList = []; // iterate and process each matched element this.each(function() { var $this = $(this); var $sObj; if (objType) { $sObj = $this.closest("[data-obj_type='" + objType + "']"); } else { $sObj = $this.closest("[data-obj_type]"); } if ($sObj.length > 0) { var objRef = { type : $sObj.attr("data-obj_type"), id : $sObj.attr("data-obj_id"), $element : $sObj }; resultList.push(objRef); } }); if (resultList.length === 0) { return null; } else if (resultList.length === 1) { return resultList[0]; } else { return resultList; } }; // --------- /old objRef code --------- // },{"./brite-dao.js":2,"./brite-utils.js":6}],5:[function(require,module,exports){ /** * @namespace * * User Agent utilities to know what capabilities the browser support. */ var useragent = {}; module.exports = useragent; var CSS_PREFIXES = {webkit:"-webkit-",chrome:"-webkit-",mozilla:"-moz-",msie:"-ms-",opera:"-o-"}; var VAR_PREFIXES = {webkit:"Webkit",mozilla:"Moz",chrome:"Webkit",msie:"ms",opera:"o"}; // privates var _cssVarPrefix = null; var _cssPrefix = null; var _cssHas = null; var _cssHasNo = null; var _hasTouch = null; var _hasTransition = null; var _hasBackfaceVisibility = null; var _hasCanvas = null; var _eventsMap = {}; // {eventName:true/false,....} var _browserType = null; // could be "webkit" "moz" "ms" "o" // --------- Get useragent.browser --------- // // Use the jquery compat code. (we still need this for the prefix) function uaMatch( ua ) { ua = ua.toLowerCase(); var match = /(chrome)[ \/]([\w.]+)/.exec( ua ) || /(webkit)[ \/]([\w.]+)/.exec( ua ) || /(opera)(?:.*version|)[ \/]([\w.]+)/.exec( ua ) || /(msie) ([\w.]+)/.exec( ua ) || ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec( ua ) || []; return { browser: match[ 1 ] || "", version: match[ 2 ] || "0" }; } var matched = uaMatch( navigator.userAgent ); var browser = {}; if ( matched.browser ) { browser[ matched.browser ] = true; browser.version = matched.version; } // Chrome is Webkit, but Webkit is also Safari. if ( browser.chrome ) { browser.webkit = true; } else if ( browser.webkit ) { browser.safari = true; } useragent.browser = browser; // --------- /Get useragent.browser --------- // // --------- Prefix and rendererType ------ // function computeBrowserType(){ $.each(CSS_PREFIXES,function(key){ if (useragent.browser[key]){ _browserType = key; _cssPrefix = CSS_PREFIXES[key]; _cssVarPrefix = VAR_PREFIXES[key]; } }); } useragent.browserType = function(){ if (_browserType === null){ computeBrowserType(); } return _browserType; }; useragent.cssPrefix = function() { if (_cssPrefix === null){ computeBrowserType(); } return _cssPrefix; }; useragent.cssVarPrefix = function() { if (_cssVarPrefix === null){ computeBrowserType(); } return _cssVarPrefix; }; // --------- /Prefix and rendererType ------ // /** * return a css friendly string with all the "has-**" that this ua supports * * @example * useragent.cssHas(); // "has-canvas has-transition" for modern PC browsers * // "has-canvas has-transition has-touch" in the case of touch devices * */ useragent.cssHas = function(){ if (_cssHas === null){ var STR = "has"; _cssHas = ""; $.each(useragent,function(key){ var fun = useragent[key]; var cssKey; if (key.indexOf(STR) === 0 && $.isFunction(fun)){ if (fun.call(useragent)){ cssKey = "has-" + key.substring(STR.length).toLowerCase(); _cssHas += cssKey + " "; } } }); } return _cssHas; }; /** * Return a css friendly version of the "no" of the has. "has-no-canvas" for example. * * @example * useragent. */ useragent.cssHasNo = function(){ if (_cssHasNo === null){ var STR = "has"; _cssHasNo = ""; $.each(useragent,function(key){ var fun = useragent[key]; var cssKey; if (key.indexOf(STR) === 0 && $.isFunction(fun)){ if (!fun.call(useragent)){ cssKey = "has-no-" + key.substring(STR.length).toLowerCase(); _cssHasNo += cssKey + " "; } } }); } return _cssHasNo; }; /** * Return true if the eventname is supported by this user agent. * * @param {Object} * eventName */ useragent.supportsEvent = function(eventName) { var r = _eventsMap[eventName]; if (typeof r === "undefined") { r = isEventSupported(eventName); _eventsMap[eventName] = r; } return r; }; /** * Convenient methods to know if this user agent supports touch events. It tests "touchstart". */ useragent.mouseOnly = false; // TODO: temporary flag to force mouseOnly (while we add the window hybrid support) useragent.hasTouch = function() { if (_hasTouch === null){ _hasTouch = (this.supportsEvent("touchstart") && !useragent.mouseOnly); } return _hasTouch; }; useragent.hasCanvas = function() { if (_hasCanvas === null) { var test_canvas = document.createElement("canvas"); _hasCanvas = (test_canvas.getContext) ? true : false; } return _hasCanvas; }; /** * Return true if the user agent supports CSS3 transition. */ useragent.hasTransition = function() { if (_hasTransition === null) { _hasTransition = hasStyle("transition","Transition","color 1s linear",true); } return _hasTransition; }; useragent.hasBackfaceVisibility = function(){ if (_hasBackfaceVisibility === null){ _hasBackfaceVisibility = hasStyle("backface-visibility","BackfaceVisibility","hidden",true); // being conservative, because, sometime windows does not support backface visibility. if (navigator.platform.toLowerCase().indexOf("win") > -1){ _hasBackfaceVisibility = false; } } return _hasBackfaceVisibility; }; // ------ Privates ------ // function hasStyle(styleName,styleVarName,sampleValue,withPrefix){ var div = document.createElement('div'); styleName = (withPrefix)?(useragent.cssPrefix() + styleName):styleName; div.innerHTML = '
'; styleVarName = (withPrefix)?(useragent.cssVarPrefix() + styleVarName):styleVarName; return (div.firstChild.style[styleVarName])?true:false; } var isEventSupported = (function() { var TAGNAMES = { 'select' : 'input', 'change' : 'input', 'submit' : 'form', 'reset' : 'form', 'error' : 'img', 'load' : 'img', 'abort' : 'img' }; function isEventSupported(eventName) { var el = document.createElement(TAGNAMES[eventName] || 'div'); eventName = 'on' + eventName; var isSupported = (eventName in el); if (!isSupported) { el.setAttribute(eventName, 'return;'); isSupported = typeof el[eventName] == 'function'; } el = null; return isSupported; } return isEventSupported; })(); // ------ /Privates ------ // },{}],6:[function(require,module,exports){ var ua = require("./brite-ua.js"); var utils = {}; module.exports = utils; utils.DOC_EVENT_NS_PREFIX = "."; utils.WIN_EVENT_NS_PREFIX = "."; // add the trim prototype if not available natively. if(!String.prototype.trim) { String.prototype.trim = function () { return this.replace(/^\s+|\s+$/g,''); }; } // default options for brite.whenEach var whenEachOpts = { failOnFirst : true }; /** * Convenient function that resolve each items serially with resolver function. * * @param {Array} items: array values to iterate through * @param {Function} resolver: will be called with resolver(value,index) and can return the result or a promise of the result. * @param {Object} opts: (optional) options with the following values * opts.failOnFirst {boolean} (default: true) if true, will reject on first fail with error object * * @return {Promise} promise that will get resolve with an Array of result of each value * * The promise is resolve with an array of result when success * * The promise is rejected with an array of {success:[true/false],value:[result/error]} * */ utils.whenEach = function(items,resolver,opts){ var dfd = $.Deferred(); var results = []; var i = 0; opts = $.extend({},whenEachOpts, opts); resolveAndNext(); function resolveAndNext(){ if (i < items.length){ var item = items[i]; var result = resolver(item,i); // if the result is a promise (but not a jquery object, which is also a promise), then, pipe it if (typeof result !== "undefined" && result !== null && $.isFunction(result.promise) && !result.jquery){ result.done(function(finalResult){ results.push(finalResult); i++; resolveAndNext(); }); // if it fails, then, reject // TODO: needs to support the failOnFirst: true result.fail(function(ex){ var fails = $.map(function(val){ return {success:true,value:val}; }); fails.push({success:false,value:ex}); dfd.reject(fails); }); // TODO: need to handle the case the promise fail } // if it is a normal object or a jqueryObject, then, just push the value and move to the next else{ results.push(result); i++; resolveAndNext(); } } // once we run out else{ dfd.resolve(results); } } return dfd.promise(); }; // Private array of chars to use var CHARS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split(''); /** * Create a random id.
*
* Code from: Math.uuid 2010 Robert Kieffer http://www.broofa.com * * * @example brite.uuid(); // returns "92329D39-6F5C-4520-ABFC-AAB64544E172" brite.uuid(15); // 15 * character ID (default base=62), returns "VcydxgltxrVZSTV" brite.uuid(8, 2); // returns "01001010" * * @param {Number} * len (optional) length in char of the returned random ID. If absent, the standard UUID format will be * returned * @param {Number} * radix (optional) radix of the random number. (Default: 62) */ utils.uuid = function(len, radix) { var chars = CHARS, uuid = []; radix = radix || chars.length; len = len || 10; for ( var i = 0; i < len; i++){ uuid[i] = chars[0 | Math.random() * radix]; } return uuid.join(''); }; /** * Return the value from this rootObj with the "." delimited path name. * * Return undefined if any of the node going down is undefined. * * @param {Object} * rootObj this is the root obj to start from * @param {String} * pathToValue this is the "." delimited path to the value * * @example brite.value({contact:{firstName:"Mike"}},"contact.firstName"); // return Mike * */ // deprecated utils.value = function(rootObj, pathToValue) { if (!rootObj) { return rootObj; } // for now, return the rootObj if the pathToValue is empty or null or undefined if (!pathToValue) { return rootObj; } var i, l, names = pathToValue.split("."); var iName, iVal = rootObj; for (i = 0, l = names.length; i < l; i++) { iName = names[i]; if (iVal == null) { return undefined; } iVal = iVal[iName]; if (typeof iVal === "undefined") { return iVal; } } return iVal; }; // substract all the values for two object (ignore the not numbers one), and return the new object. // deprecated utils.substract = function(obj1,obj2){ var r = {}; $.each(obj1,function(key,val1){ var val2 = obj2[key]; if (!isNaN(val1) && !isNaN(val2)){ r[key] = val1 - val2; } }); return r; }; // add all the values for two object (ignore the not numbers one), and return the new object. // deprecated utils.add = function(obj1,obj2){ var r = {}; $.each(obj1,function(key,val1){ var val2 = obj2[key]; if (!isNaN(val1) && !isNaN(val2)){ r[key] = val1 + val2; } }); return r; }; /** * @namespace * * Array utilities */ // deprecated utils.array = { /** * Remove item(s) from an array. * Code from: Array Remove - By John Resig (MIT Licensed) * * @param {Object} * a the Array * @param {Object} * from the first index to remove from * @param {Object} * to (optional) the last index to remove */ remove : function(a, from, to) { var rest = a.slice((to || from) + 1 || a.length); a.length = from < 0 ? a.length + from : from; return a.push.apply(a, rest); }, /** * For a array of object, this will get the first index of the matching prop name/value return -1 if no match * * @param {Object} * a the Array * @param {Object} * propName the property name * @param {Object} * propValue the property value to be matched */ getIndex : function(a, propName, propValue) { if (a && propName && typeof propValue != "undefined") { var i, obj, l = a.length; for (i = 0; i < l; i++) { obj = a[i]; if (obj && obj[propName] === propValue) { return i; } } } return -1; }, getItem : function(a, propName, propValue){ var idx = this.getIndex(a,propName,propValue); if (idx > -1){ return a[idx]; }else{ return null; } }, /** * Sort an array of object by a propName * * @param {Object} * a the Array * @param {Object} * propName the property name to be sorted by */ sortBy : function(a, propName) { return a.sort(sortByFunc); function sortByFunc(a, b) { if (typeof a === "undefined") return -1; if (typeof b === "undefined") return 1; var x = a[propName]; var y = b[propName]; return ((x < y) ? -1 : ((x > y) ? 1 : 0)); } }, /** * From an array of javascript obect, create a map (js object) where the key is the propName value, and the * value is the array item. If the propName does not on an item exist, it will ingore the item. * * @example var myVehicules = [{id:"truck",speed:80},{id:"racecar",speed:200}]; * var vehiculeById = brite.array.toMap(myVehicules,"id"); // vehiculeById["truck"].speed == 80 * * @param {Object} * a The array * @param {Object} * keyName the property name that will be use */ toMap : function(a, keyName) { var i, l = a.length; var map = {}, item, key; for (i = 0; i < l; i++) { item = a[i]; key = item[keyName]; if (typeof key != "undefined" && key != null) { map[key] = item; } } return map; } }; /** * Give a random number between two number * * @param {Object} * from * @param {Object} * to */ // deprecated utils.randomInt = function(from, to) { var offset = to - from; return from + Math.floor(Math.random() * (offset + 1)); }; // from the "JavaScript Pattern" book utils.inherit = function(C, P) { var F = function() { }; F.prototype = P.prototype; C.prototype = new F(); C._super = P.prototype; C.prototype.constructor = C; }; // hack to force the browsers on mobile devices to redraw // basically, it is a visually invisible div, but technically in the display tree // that we change the content and css property (width). This seems to force the browser to refresh var _flushUIVar = 2; var _$flushUI; utils.flushUI = function(){ if (ua.hasTouch()){ if (!_$flushUI){ _$flushUI = $("
flushUI
"); $("body").append(_$flushUI); } _flushUIVar = _flushUIVar * -1; _$flushUI.text("").text(_flushUIVar); _$flushUI.css("width",_flushUIVar + "px"); } }; },{"./brite-ua.js":5}],7:[function(require,module,exports){ var core = require("./brite-core.js"); var ua = require("./brite-ua.js"); var dao = require("./brite-dao.js"); var utils = require("./brite-utils.js"); var event = require("./brite-event.js"); require("./brite-jquery-plugins.js"); var version = "1.1.6"; module.exports = { version: version, // views registerView: core.registerView, display: core.display, attach: core.attach, config: core.config, viewDefaultConfig: core.viewDefaultConfig, // dao dao: dao.dao, // true, little weird, will cleanup during deprecation registerDao: dao.registerDao, triggerDataChange: dao.triggerDataChange, // event event: event, // user agent ua: ua, // utils whenEach: utils.whenEach, uuid: utils.uuid, value: utils.value, substract: utils.substract, add: utils.add, array: utils.array, randomInt: utils.randomInt, inherit: utils.inherit, flushUI: utils.flushUI, // deprecated registerTransition: core.registerTransition, getTransition: core.getTransition, legacyDisplay: core.legacyDisplay, defaultComponentConfig: core.viewDefaultConfig, registerComponent: core.registerView }; // for AMD component support if ( typeof define === "function" && define.amd ) { define( "brite", [], function () { return brite; } ); } // when in browser environment, put brite in the global scope (to make britejs easier to include separatly) if (typeof window !== "undefined"){ window.brite = module.exports; } },{"./brite-core.js":1,"./brite-dao.js":2,"./brite-event.js":3,"./brite-jquery-plugins.js":4,"./brite-ua.js":5,"./brite-utils.js":6}]},{},[7]);