PageRenderTime 62ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 1ms

/dev/_lib/Fluid/3akai_Infusion.js

https://github.com/zqian/3akai-ux
JavaScript | 8226 lines | 6558 code | 671 blank | 997 comment | 642 complexity | 91d89959cfdaf5cbbe73468da5303662 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1. /* 3akai_Infusion.js
  2. * custom build for Sakai
  3. * built with: ant customBuild -Dinclude="uploader, reorderer" -Dexclude="jQuery, jQueryUICore, jQueryUIWidgets, jQueryTooltipPlugin" -Djsfilename="3akai_Infusion.js" -DnoMinify="true"
  4. */
  5. /*
  6. * jQuery delegate plug-in v1.0
  7. *
  8. * Copyright (c) 2007 Jörn Zaefferer
  9. *
  10. * $Id: jquery.delegate.js 7175 2009-05-15 16:59:09Z antranig@caret.cam.ac.uk $
  11. *
  12. * Dual licensed under the MIT and GPL licenses:
  13. * http://www.opensource.org/licenses/mit-license.php
  14. * http://www.gnu.org/licenses/gpl.html
  15. */
  16. // provides cross-browser focusin and focusout events
  17. // IE has native support, in other browsers, use event caputuring (neither bubbles)
  18. // provides delegate(type: String, delegate: Selector, handler: Callback) plugin for easier event delegation
  19. // handler is only called when $(event.target).is(delegate), in the scope of the jQuery-object for event.target
  20. // provides triggerEvent(type: String, target: Element) to trigger delegated events
  21. ;(function($) {
  22. $.each({
  23. focus: 'focusin',
  24. blur: 'focusout'
  25. }, function( original, fix ){
  26. $.event.special[fix] = {
  27. setup:function() {
  28. if ( $.browser.msie ) return false;
  29. this.addEventListener( original, $.event.special[fix].handler, true );
  30. },
  31. teardown:function() {
  32. if ( $.browser.msie ) return false;
  33. this.removeEventListener( original,
  34. $.event.special[fix].handler, true );
  35. },
  36. handler: function(e) {
  37. arguments[0] = $.event.fix(e);
  38. arguments[0].type = fix;
  39. return $.event.handle.apply(this, arguments);
  40. }
  41. };
  42. });
  43. $.extend($.fn, {
  44. delegate: function(type, delegate, handler) {
  45. return this.bind(type, function(event) {
  46. var target = $(event.target);
  47. if (target.is(delegate)) {
  48. return handler.apply(target, arguments);
  49. }
  50. });
  51. },
  52. triggerEvent: function(type, target) {
  53. return this.triggerHandler(type, [jQuery.event.fix({ type: type, target: target })]);
  54. }
  55. })
  56. })(jQuery);
  57. /*
  58. Copyright 2007-2010 University of Cambridge
  59. Copyright 2007-2009 University of Toronto
  60. Copyright 2007-2009 University of California, Berkeley
  61. Licensed under the Educational Community License (ECL), Version 2.0 or the New
  62. BSD license. You may not use this file except in compliance with one these
  63. Licenses.
  64. You may obtain a copy of the ECL 2.0 License and BSD License at
  65. https://source.fluidproject.org/svn/LICENSE.txt
  66. */
  67. // Declare dependencies.
  68. /*global jQuery, YAHOO, opera*/
  69. var fluid_1_2 = fluid_1_2 || {};
  70. var fluid = fluid || fluid_1_2;
  71. (function ($, fluid) {
  72. fluid.version = "Infusion 1.2";
  73. fluid.environment = {
  74. fluid: fluid
  75. };
  76. var globalObject = window || {};
  77. /**
  78. * Causes an error message to be logged to the console and a real runtime error to be thrown.
  79. *
  80. * @param {String|Error} message the error message to log
  81. */
  82. fluid.fail = function (message) {
  83. fluid.setLogging(true);
  84. fluid.log(message.message? message.message : message);
  85. throw new Error(message);
  86. //message.fail(); // Intentionally cause a browser error by invoking a nonexistent function.
  87. };
  88. /**
  89. * Wraps an object in a jQuery if it isn't already one. This function is useful since
  90. * it ensures to wrap a null or otherwise falsy argument to itself, rather than the
  91. * often unhelpful jQuery default of returning the overall document node.
  92. *
  93. * @param {Object} obj the object to wrap in a jQuery
  94. */
  95. fluid.wrap = function (obj) {
  96. return ((!obj || obj.jquery) ? obj : $(obj));
  97. };
  98. /**
  99. * If obj is a jQuery, this function will return the first DOM element within it.
  100. *
  101. * @param {jQuery} obj the jQuery instance to unwrap into a pure DOM element
  102. */
  103. fluid.unwrap = function (obj) {
  104. return obj && obj.jquery && obj.length === 1 ? obj[0] : obj; // Unwrap the element if it's a jQuery.
  105. };
  106. /**
  107. * Searches through the supplied object for the first value which matches the one supplied.
  108. * @param obj {Object} the Object to be searched through
  109. * @param value {Object} the value to be found. This will be compared against the object's
  110. * member using === equality.
  111. * @return {String} The first key whose value matches the one supplied, or <code>null</code> if no
  112. * such key is found.
  113. */
  114. fluid.keyForValue = function (obj, value) {
  115. for (var key in obj) {
  116. if (obj[key] === value) {
  117. return key;
  118. }
  119. }
  120. return null;
  121. };
  122. /**
  123. * This method is now deprecated and will be removed in a future release of Infusion.
  124. * See fluid.keyForValue instead.
  125. */
  126. fluid.findKeyInObject = fluid.keyForValue;
  127. /**
  128. * Clears an object or array of its contents. For objects, each property is deleted.
  129. *
  130. * @param {Object|Array} target the target to be cleared
  131. */
  132. fluid.clear = function (target) {
  133. if (target instanceof Array) {
  134. target.length = 0;
  135. }
  136. else {
  137. for (var i in target) {
  138. delete target[i];
  139. }
  140. }
  141. };
  142. /** A basic utility that returns its argument unchanged */
  143. fluid.identity = function(arg) {
  144. return arg;
  145. }
  146. // Framework and instantiation functions.
  147. /**
  148. * Fetches a single container element and returns it as a jQuery.
  149. *
  150. * @param {String||jQuery||element} an id string, a single-element jQuery, or a DOM element specifying a unique container
  151. * @return a single-element jQuery of container
  152. */
  153. fluid.container = function (containerSpec) {
  154. var container = containerSpec;
  155. if (typeof containerSpec === "string" ||
  156. containerSpec.nodeType && (containerSpec.nodeType === 1 || containerSpec.nodeType === 9)) {
  157. container = $(containerSpec);
  158. }
  159. // Throw an exception if we've got more or less than one element.
  160. if (!container || !container.jquery || container.length !== 1) {
  161. if (typeof(containerSpec) !== "string") {
  162. containerSpec = container.selector;
  163. }
  164. fluid.fail({
  165. name: "NotOne",
  166. message: "A single container element was not found for selector " + containerSpec
  167. });
  168. }
  169. return container;
  170. };
  171. // stubs for two functions in FluidDebugging.js
  172. fluid.dumpEl = fluid.identity;
  173. fluid.renderTimestamp = fluid.identity;
  174. /**
  175. * Retreives and stores a component's default settings centrally.
  176. * @param {boolean} (options) if true, manipulate a global option (for the head
  177. * component) rather than instance options.
  178. * @param {String} componentName the name of the component
  179. * @param {Object} (optional) an container of key/value pairs to set
  180. *
  181. */
  182. var defaultsStore = {};
  183. var globalDefaultsStore = {};
  184. fluid.defaults = function () {
  185. var offset = 0;
  186. var store = defaultsStore;
  187. if (typeof arguments[0] === "boolean") {
  188. store = globalDefaultsStore;
  189. offset = 1;
  190. }
  191. var componentName = arguments[offset];
  192. var defaultsObject = arguments[offset + 1];
  193. if (defaultsObject !== undefined) {
  194. store[componentName] = defaultsObject;
  195. return defaultsObject;
  196. }
  197. return store[componentName];
  198. };
  199. /**
  200. * Creates a new DOM Binder instance, used to locate elements in the DOM by name.
  201. *
  202. * @param {Object} container the root element in which to locate named elements
  203. * @param {Object} selectors a collection of named jQuery selectors
  204. */
  205. fluid.createDomBinder = function (container, selectors) {
  206. var cache = {}, that = {};
  207. function cacheKey(name, thisContainer) {
  208. return fluid.allocateSimpleId(thisContainer) + "-" + name;
  209. }
  210. function record(name, thisContainer, result) {
  211. cache[cacheKey(name, thisContainer)] = result;
  212. }
  213. that.locate = function (name, localContainer) {
  214. var selector, thisContainer, togo;
  215. selector = selectors[name];
  216. thisContainer = localContainer? localContainer: container;
  217. if (!thisContainer) {
  218. fluid.fail("DOM binder invoked for selector " + name + " without container");
  219. }
  220. if (!selector) {
  221. return thisContainer;
  222. }
  223. if (typeof(selector) === "function") {
  224. togo = $(selector.call(null, fluid.unwrap(thisContainer)));
  225. } else {
  226. togo = $(selector, thisContainer);
  227. }
  228. if (togo.get(0) === document) {
  229. togo = [];
  230. //fluid.fail("Selector " + name + " with value " + selectors[name] +
  231. // " did not find any elements with container " + fluid.dumpEl(container));
  232. }
  233. if (!togo.selector) {
  234. togo.selector = selector;
  235. togo.context = thisContainer;
  236. }
  237. togo.selectorName = name;
  238. record(name, thisContainer, togo);
  239. return togo;
  240. };
  241. that.fastLocate = function (name, localContainer) {
  242. var thisContainer = localContainer? localContainer: container;
  243. var key = cacheKey(name, thisContainer);
  244. var togo = cache[key];
  245. return togo? togo : that.locate(name, localContainer);
  246. };
  247. that.clear = function () {
  248. cache = {};
  249. };
  250. that.refresh = function (names, localContainer) {
  251. var thisContainer = localContainer? localContainer: container;
  252. if (typeof names === "string") {
  253. names = [names];
  254. }
  255. if (thisContainer.length === undefined) {
  256. thisContainer = [thisContainer];
  257. }
  258. for (var i = 0; i < names.length; ++ i) {
  259. for (var j = 0; j < thisContainer.length; ++ j) {
  260. that.locate(names[i], thisContainer[j]);
  261. }
  262. }
  263. };
  264. return that;
  265. };
  266. /** Determines whether the supplied object can be treated as an array, by
  267. * iterating an index towards its length. The test functions by detecting
  268. * a property named "length" which is of type "number", but excluding objects
  269. * which are themselves of type "string".
  270. */
  271. fluid.isArrayable = function(totest) {
  272. return typeof(totest) !== "string" && typeof(totest.length) === "number";
  273. }
  274. /**
  275. * Attaches the user's listeners to a set of events.
  276. *
  277. * @param {Object} events a collection of named event firers
  278. * @param {Object} listeners optional listeners to add
  279. */
  280. fluid.mergeListeners = function (events, listeners) {
  281. if (listeners) {
  282. for (var key in listeners) {
  283. var value = listeners[key];
  284. var keydot = key.indexOf(".");
  285. var namespace;
  286. if (keydot !== -1) {
  287. namespace = key.substring(keydot + 1);
  288. key = key.substring(0, keydot);
  289. }
  290. if (!events[key]) {
  291. events[key] = fluid.event.getEventFirer();
  292. }
  293. var firer = events[key];
  294. if (typeof(value) === "function") {
  295. firer.addListener(value, namespace);
  296. }
  297. else if (value && fluid.isArrayable(value)) {
  298. for (var i = 0; i < value.length; ++ i) {
  299. firer.addListener(value[i], namespace);
  300. }
  301. }
  302. }
  303. }
  304. };
  305. /**
  306. * Sets up a component's declared events.
  307. * Events are specified in the options object by name. There are three different types of events that can be
  308. * specified:
  309. * 1. an ordinary multicast event, specified by "null.
  310. * 2. a unicast event, which allows only one listener to be registered
  311. * 3. a preventable event
  312. *
  313. * @param {Object} that the component
  314. * @param {Object} options the component's options structure, containing the declared event names and types
  315. */
  316. fluid.instantiateFirers = function (that, options) {
  317. that.events = {};
  318. if (options.events) {
  319. for (var event in options.events) {
  320. var eventType = options.events[event];
  321. that.events[event] = fluid.event.getEventFirer(eventType === "unicast", eventType === "preventable");
  322. }
  323. }
  324. fluid.mergeListeners(that.events, options.listeners);
  325. };
  326. /**
  327. * Merges the component's declared defaults, as obtained from fluid.defaults(),
  328. * with the user's specified overrides.
  329. *
  330. * @param {Object} that the instance to attach the options to
  331. * @param {String} componentName the unique "name" of the component, which will be used
  332. * to fetch the default options from store. By recommendation, this should be the global
  333. * name of the component's creator function.
  334. * @param {Object} userOptions the user-specified configuration options for this component
  335. */
  336. fluid.mergeComponentOptions = function (that, componentName, userOptions) {
  337. var defaults = fluid.defaults(componentName);
  338. that.options = fluid.merge(defaults? defaults.mergePolicy: null, {}, defaults, userOptions);
  339. };
  340. /** Expect that an output from the DOM binder has resulted in a non-empty set of
  341. * results. If none are found, this function will fail with a diagnostic message,
  342. * with the supplied message prepended.
  343. */
  344. fluid.expectFilledSelector = function (result, message) {
  345. if (result && result.length === 0 && result.jquery) {
  346. fluid.fail(message + ": selector \"" + result.selector + "\" with name " + result.selectorName +
  347. " returned no results in context " + fluid.dumpEl(result.context));
  348. }
  349. };
  350. /**
  351. * The central initialiation method called as the first act of every Fluid
  352. * component. This function automatically merges user options with defaults,
  353. * attaches a DOM Binder to the instance, and configures events.
  354. *
  355. * @param {String} componentName The unique "name" of the component, which will be used
  356. * to fetch the default options from store. By recommendation, this should be the global
  357. * name of the component's creator function.
  358. * @param {jQueryable} container A specifier for the single root "container node" in the
  359. * DOM which will house all the markup for this component.
  360. * @param {Object} userOptions The configuration options for this component.
  361. */
  362. fluid.initView = function (componentName, container, userOptions) {
  363. var that = {};
  364. fluid.expectFilledSelector(container, "Error instantiating component with name \"" + componentName);
  365. fluid.mergeComponentOptions(that, componentName, userOptions);
  366. if (container) {
  367. that.container = fluid.container(container);
  368. fluid.initDomBinder(that);
  369. }
  370. fluid.instantiateFirers(that, that.options);
  371. return that;
  372. };
  373. /** A special "marker object" which is recognised as one of the arguments to
  374. * fluid.initSubcomponents. This object is recognised by reference equality -
  375. * where it is found, it is replaced in the actual argument position supplied
  376. * to the specific subcomponent instance, with the particular options block
  377. * for that instance attached to the overall "that" object.
  378. */
  379. fluid.COMPONENT_OPTIONS = {};
  380. /** Another special "marker object" representing that a distinguished
  381. * (probably context-dependent) value should be substituted.
  382. */
  383. fluid.VALUE = {};
  384. /** Construct a dummy or "placeholder" subcomponent, that optionally provides empty
  385. * implementations for a set of methods.
  386. */
  387. fluid.emptySubcomponent = function (options) {
  388. var that = {};
  389. options = $.makeArray(options);
  390. for (var i = 0; i < options.length; ++ i) {
  391. that[options[i]] = function () {};
  392. }
  393. return that;
  394. };
  395. /**
  396. * Creates a new "little component": a that-ist object with options merged into it by the framework.
  397. * This method is a convenience for creating small objects that have options but don't require full
  398. * View-like features such as the DOM Binder or events
  399. *
  400. * @param {Object} name the name of the little component to create
  401. * @param {Object} options user-supplied options to merge with the defaults
  402. */
  403. fluid.initLittleComponent = function(name, options) {
  404. var that = {};
  405. fluid.mergeComponentOptions(that, name, options);
  406. return that;
  407. };
  408. fluid.initSubcomponent = function (that, className, args) {
  409. return fluid.initSubcomponents(that, className, args)[0];
  410. };
  411. /** Initialise all the "subcomponents" which are configured to be attached to
  412. * the supplied top-level component, which share a particular "class name".
  413. * @param {Component} that The top-level component for which sub-components are
  414. * to be instantiated. It contains specifications for these subcomponents in its
  415. * <code>options</code> structure.
  416. * @param {String} className The "class name" or "category" for the subcomponents to
  417. * be instantiated. A class name specifies an overall "function" for a class of
  418. * subcomponents and represents a category which accept the same signature of
  419. * instantiation arguments.
  420. * @param {Array of Object} args The instantiation arguments to be passed to each
  421. * constructed subcomponent. These will typically be members derived from the
  422. * top-level <code>that</code> or perhaps globally discovered from elsewhere. One
  423. * of these arguments may be <code>fluid.COMPONENT_OPTIONS</code> in which case this
  424. * placeholder argument will be replaced by instance-specific options configured
  425. * into the member of the top-level <code>options</code> structure named for the
  426. * <code>className</code>
  427. * @return {Array of Object} The instantiated subcomponents, one for each member
  428. * of <code>that.options[className]</code>.
  429. */
  430. fluid.initSubcomponents = function (that, className, args) {
  431. var entry = that.options[className];
  432. if (!entry) {
  433. return;
  434. }
  435. var entries = $.makeArray(entry);
  436. var optindex = -1;
  437. var togo = [];
  438. args = $.makeArray(args);
  439. for (var i = 0; i < args.length; ++ i) {
  440. if (args[i] === fluid.COMPONENT_OPTIONS) {
  441. optindex = i;
  442. }
  443. }
  444. for (i = 0; i < entries.length; ++ i) {
  445. entry = entries[i];
  446. if (optindex !== -1 && entry.options) {
  447. args[optindex] = entry.options;
  448. }
  449. if (typeof(entry) !== "function") {
  450. var entryType = typeof(entry) === "string"? entry : entry.type;
  451. var globDef = fluid.defaults(true, entryType);
  452. fluid.merge("reverse", that.options, globDef);
  453. togo[i] = entryType === "fluid.emptySubcomponent"?
  454. fluid.emptySubcomponent(entry.options) :
  455. fluid.invokeGlobalFunction(entryType, args, {fluid: fluid});
  456. }
  457. else {
  458. togo[i] = entry.apply(null, args);
  459. }
  460. var returnedOptions = togo[i]? togo[i].returnedOptions : null;
  461. if (returnedOptions) {
  462. fluid.merge(that.options.mergePolicy, that.options, returnedOptions);
  463. if (returnedOptions.listeners) {
  464. fluid.mergeListeners(that.events, returnedOptions.listeners);
  465. }
  466. }
  467. }
  468. return togo;
  469. };
  470. /**
  471. * Creates a new DOM Binder instance for the specified component and mixes it in.
  472. *
  473. * @param {Object} that the component instance to attach the new DOM Binder to
  474. */
  475. fluid.initDomBinder = function (that) {
  476. that.dom = fluid.createDomBinder(that.container, that.options.selectors);
  477. that.locate = that.dom.locate;
  478. };
  479. /** Returns true if the argument is a primitive type **/
  480. fluid.isPrimitive = function (value) {
  481. var valueType = typeof(value);
  482. return !value || valueType === "string" || valueType === "boolean" || valueType === "number" || valueType === "function";
  483. };
  484. function mergeImpl(policy, basePath, target, source) {
  485. var thisPolicy = policy && typeof(policy) !== "string"? policy[basePath] : policy;
  486. if (typeof(thisPolicy) === "function") {
  487. thisPolicy.apply(null, target, source);
  488. return target;
  489. }
  490. if (thisPolicy === "replace") {
  491. fluid.clear(target);
  492. }
  493. for (var name in source) {
  494. var path = (basePath? basePath + ".": "") + name;
  495. var thisTarget = target[name];
  496. var thisSource = source[name];
  497. var primitiveTarget = fluid.isPrimitive(thisTarget);
  498. if (thisSource !== undefined) {
  499. if (thisSource !== null && typeof thisSource === 'object' &&
  500. !thisSource.nodeType && !thisSource.jquery && thisSource !== fluid.VALUE) {
  501. if (primitiveTarget) {
  502. target[name] = thisTarget = thisSource instanceof Array? [] : {};
  503. }
  504. mergeImpl(policy, path, thisTarget, thisSource);
  505. }
  506. else {
  507. if (thisTarget === null || thisTarget === undefined || thisPolicy !== "reverse") {
  508. target[name] = thisSource;
  509. }
  510. }
  511. }
  512. }
  513. return target;
  514. }
  515. /** Merge a collection of options structures onto a target, following an optional policy.
  516. * This function is typically called automatically, as a result of an invocation of
  517. * <code>fluid.iniView</code>. The behaviour of this function is explained more fully on
  518. * the page http://wiki.fluidproject.org/display/fluid/Options+Merging+for+Fluid+Components .
  519. * @param policy {Object/String} A "policy object" specifiying the type of merge to be performed.
  520. * If policy is of type {String} it should take on the value "reverse" or "replace" representing
  521. * a static policy. If it is an
  522. * Object, it should contain a mapping of EL paths onto these String values, representing a
  523. * fine-grained policy. If it is an Object, the values may also themselves be EL paths
  524. * representing that a default value is to be taken from that path.
  525. * @param target {Object} The options structure which is to be modified by receiving the merge results.
  526. * @param options1, options2, .... {Object} an arbitrary list of options structure which are to
  527. * be merged "on top of" the <code>target</code>. These will not be modified.
  528. */
  529. fluid.merge = function (policy, target) {
  530. var path = "";
  531. for (var i = 2; i < arguments.length; ++i) {
  532. var source = arguments[i];
  533. if (source !== null && source !== undefined) {
  534. mergeImpl(policy, path, target, source);
  535. }
  536. }
  537. if (policy && typeof(policy) !== "string") {
  538. for (var key in policy) {
  539. var elrh = policy[key];
  540. if (typeof(elrh) === 'string' && elrh !== "replace") {
  541. var oldValue = fluid.model.getBeanValue(target, key);
  542. if (oldValue === null || oldValue === undefined) {
  543. var value = fluid.model.getBeanValue(target, elrh);
  544. fluid.model.setBeanValue(target, key, value);
  545. }
  546. }
  547. }
  548. }
  549. return target;
  550. };
  551. /** Return an empty container as the same type as the argument (either an
  552. * array or hash */
  553. fluid.freshContainer = function(tocopy) {
  554. return fluid.isArrayable(tocopy)? [] : {};
  555. };
  556. /** Performs a deep copy (clone) of its argument **/
  557. fluid.copy = function (tocopy) {
  558. if (fluid.isPrimitive(tocopy)) {
  559. return tocopy;
  560. }
  561. return $.extend(true, fluid.freshContainer(tocopy), tocopy);
  562. };
  563. fluid.getGlobalValue = function(path, env) {
  564. env = env || fluid.environment;
  565. return fluid.model.getBeanValue(globalObject, path, env);
  566. };
  567. /**
  568. * Allows for the calling of a function from an EL expression "functionPath", with the arguments "args", scoped to an framework version "environment".
  569. * @param {Object} functionPath - An EL expression
  570. * @param {Object} args - An array of arguments to be applied to the function, specified in functionPath
  571. * @param {Object} environment - (optional) The object to scope the functionPath to (typically the framework root for version control)
  572. */
  573. fluid.invokeGlobalFunction = function (functionPath, args, environment) {
  574. var func = fluid.getGlobalValue(functionPath, environment);
  575. if (!func) {
  576. fluid.fail("Error invoking global function: " + functionPath + " could not be located");
  577. } else {
  578. return func.apply(null, args);
  579. }
  580. };
  581. /** Registers a new global function at a given path (currently assumes that
  582. * it lies within the fluid namespace)
  583. */
  584. fluid.registerGlobalFunction = function (functionPath, func, env) {
  585. env = env || fluid.environment;
  586. fluid.model.setBeanValue(globalObject, functionPath, func, env);
  587. };
  588. fluid.registerGlobal = fluid.registerGlobalFunction;
  589. /** Ensures that an entry in the global namespace exists **/
  590. fluid.registerNamespace = function (naimspace, env) {
  591. env = env || fluid.environment;
  592. var existing = fluid.getGlobalValue(naimspace, env);
  593. if (!existing) {
  594. existing = {};
  595. fluid.registerGlobal(naimspace, existing, env);
  596. }
  597. return existing;
  598. };
  599. // The Model Events system.
  600. fluid.event = {};
  601. var fluid_guid = 1;
  602. /** Construct an "event firer" object which can be used to register and deregister
  603. * listeners, to which "events" can be fired. These events consist of an arbitrary
  604. * function signature. General documentation on the Fluid events system is at
  605. * http://wiki.fluidproject.org/display/fluid/The+Fluid+Event+System .
  606. * @param {Boolean} unicast If <code>true</code>, this is a "unicast" event which may only accept
  607. * a single listener.
  608. * @param {Boolean} preventable If <code>true</code> the return value of each handler will
  609. * be checked for <code>false</code> in which case further listeners will be shortcircuited, and this
  610. * will be the return value of fire()
  611. */
  612. fluid.event.getEventFirer = function (unicast, preventable) {
  613. var log = fluid.log;
  614. var listeners = {};
  615. return {
  616. addListener: function (listener, namespace, predicate) {
  617. if (!listener) {
  618. return;
  619. }
  620. if (unicast) {
  621. namespace = "unicast";
  622. }
  623. if (!namespace) {
  624. if (!listener.$$guid) {
  625. listener.$$guid = fluid_guid++;
  626. }
  627. namespace = listener.$$guid;
  628. }
  629. listeners[namespace] = {listener: listener, predicate: predicate};
  630. },
  631. removeListener: function (listener) {
  632. if (typeof(listener) === 'string') {
  633. delete listeners[listener];
  634. }
  635. else if (typeof(listener) === 'object' && listener.$$guid) {
  636. delete listeners[listener.$$guid];
  637. }
  638. },
  639. fire: function () {
  640. for (var i in listeners) {
  641. var lisrec = listeners[i];
  642. var listener = lisrec.listener;
  643. if (lisrec.predicate && !lisrec.predicate(listener, arguments)) {
  644. continue;
  645. }
  646. try {
  647. var ret = listener.apply(null, arguments);
  648. if (preventable && ret === false) {
  649. return false;
  650. }
  651. }
  652. catch (e) {
  653. log("FireEvent received exception " + e.message + " e " + e + " firing to listener " + i);
  654. throw (e);
  655. }
  656. }
  657. }
  658. };
  659. };
  660. // Model functions
  661. fluid.model = {};
  662. /** Copy a source "model" onto a target **/
  663. fluid.model.copyModel = function (target, source) {
  664. fluid.clear(target);
  665. $.extend(true, target, source);
  666. };
  667. /** Parse an EL expression separated by periods (.) into its component segments.
  668. * @param {String} EL The EL expression to be split
  669. * @return {Array of String} the component path expressions.
  670. * TODO: This needs to be upgraded to handle (the same) escaping rules (as RSF), so that
  671. * path segments containing periods and backslashes etc. can be processed.
  672. */
  673. fluid.model.parseEL = function (EL) {
  674. return String(EL).split('.');
  675. };
  676. fluid.model.composePath = function (prefix, suffix) {
  677. return prefix === ""? suffix : prefix + "." + suffix;
  678. };
  679. fluid.model.getPenultimate = function (root, EL, environment, create) {
  680. var segs = fluid.model.parseEL(EL);
  681. for (var i = 0; i < segs.length - 1; ++i) {
  682. if (!root) {
  683. return root;
  684. }
  685. var segment = segs[i];
  686. if (environment && environment[segment]) {
  687. root = environment[segment];
  688. environment = null;
  689. }
  690. else {
  691. if (root[segment] === undefined && create) {
  692. root[segment] = {};
  693. }
  694. root = root[segment];
  695. }
  696. }
  697. return {root: root, last: segs[segs.length - 1]};
  698. };
  699. fluid.model.setBeanValue = function (root, EL, newValue, environment) {
  700. var pen = fluid.model.getPenultimate(root, EL, environment, true);
  701. pen.root[pen.last] = newValue;
  702. };
  703. /** Evaluates an EL expression by fetching a dot-separated list of members
  704. * recursively from a provided root.
  705. * @param root The root data structure in which the EL expression is to be evaluated
  706. * @param {string} EL The EL expression to be evaluated
  707. * @param environment An optional "environment" which, if it contains any members
  708. * at top level, will take priority over the root data structure.
  709. * @return The fetched data value.
  710. */
  711. fluid.model.getBeanValue = function (root, EL, environment) {
  712. if (EL === "" || EL === null || EL === undefined) {
  713. return root;
  714. }
  715. var pen = fluid.model.getPenultimate(root, EL, environment);
  716. return pen.root? pen.root[pen.last] : pen.root;
  717. };
  718. // Logging
  719. var logging;
  720. /** method to allow user to enable logging (off by default) */
  721. fluid.setLogging = function (enabled) {
  722. if (typeof enabled === "boolean") {
  723. logging = enabled;
  724. } else {
  725. logging = false;
  726. }
  727. };
  728. /** Log a message to a suitable environmental console. If the standard "console"
  729. * stream is available, the message will be sent there - otherwise either the
  730. * YAHOO logger or the Opera "postError" stream will be used. Logging must first
  731. * be enabled with a call fo the fluid.setLogging(true) function.
  732. */
  733. fluid.log = function (str) {
  734. if (logging) {
  735. str = fluid.renderTimestamp(new Date()) + ": " + str;
  736. if (typeof(console) !== "undefined") {
  737. if (console.debug) {
  738. console.debug(str);
  739. } else {
  740. console.log(str);
  741. }
  742. }
  743. else if (typeof(YAHOO) !== "undefined") {
  744. YAHOO.log(str);
  745. }
  746. else if (typeof(opera) !== "undefined") {
  747. opera.postError(str);
  748. }
  749. }
  750. };
  751. // DOM Utilities.
  752. /**
  753. * Finds the nearest ancestor of the element that passes the test
  754. * @param {Element} element DOM element
  755. * @param {Function} test A function which takes an element as a parameter and return true or false for some test
  756. */
  757. fluid.findAncestor = function (element, test) {
  758. element = fluid.unwrap(element);
  759. while (element) {
  760. if (test(element)) {
  761. return element;
  762. }
  763. element = element.parentNode;
  764. }
  765. };
  766. /**
  767. * Returns a jQuery object given the id of a DOM node. In the case the element
  768. * is not found, will return an empty list.
  769. */
  770. fluid.jById = function (id, dokkument) {
  771. dokkument = dokkument && dokkument.nodeType === 9? dokkument : document;
  772. var element = fluid.byId(id, dokkument);
  773. var togo = element? $(element) : [];
  774. togo.selector = "#" + id;
  775. togo.context = dokkument;
  776. return togo;
  777. };
  778. /**
  779. * Returns an DOM element quickly, given an id
  780. *
  781. * @param {Object} id the id of the DOM node to find
  782. * @param {Document} dokkument the document in which it is to be found (if left empty, use the current document)
  783. * @return The DOM element with this id, or null, if none exists in the document.
  784. */
  785. fluid.byId = function (id, dokkument) {
  786. dokkument = dokkument && dokkument.nodeType === 9? dokkument : document;
  787. var el = dokkument.getElementById(id);
  788. if (el) {
  789. if (el.getAttribute("id") !== id) {
  790. fluid.fail("Problem in document structure - picked up element " +
  791. fluid.dumpEl(el) +
  792. " for id " +
  793. id +
  794. " without this id - most likely the element has a name which conflicts with this id");
  795. }
  796. return el;
  797. }
  798. else {
  799. return null;
  800. }
  801. };
  802. /**
  803. * Returns the id attribute from a jQuery or pure DOM element.
  804. *
  805. * @param {jQuery||Element} element the element to return the id attribute for
  806. */
  807. fluid.getId = function (element) {
  808. return fluid.unwrap(element).getAttribute("id");
  809. };
  810. /**
  811. * Allocate an id to the supplied element if it has none already, by a simple
  812. * scheme resulting in ids "fluid-id-nnnn" where nnnn is an increasing integer.
  813. */
  814. fluid.allocateSimpleId = function (element) {
  815. element = fluid.unwrap(element);
  816. if (!element.id) {
  817. element.id = "fluid-id-" + (fluid_guid++);
  818. }
  819. return element.id;
  820. };
  821. // Functional programming utilities.
  822. function transformInternal(source, togo, key, args) {
  823. var transit = source[key];
  824. for (var j = 0; j < args.length - 1; ++ j) {
  825. transit = args[j + 1](transit, key);
  826. }
  827. togo[key] = transit;
  828. }
  829. /** Return a list or hash of objects, transformed by one or more functions. Similar to
  830. * jQuery.map, only will accept an arbitrary list of transformation functions and also
  831. * works on non-arrays.
  832. * @param list {Array or Object} The initial container of objects to be transformed.
  833. * @param fn1, fn2, etc. {Function} An arbitrary number of optional further arguments,
  834. * all of type Function, accepting the signature (object, index), where object is the
  835. * list member to be transformed, and index is its list index. Each function will be
  836. * applied in turn to each list member, which will be replaced by the return value
  837. * from the function.
  838. * @return The finally transformed list, where each member has been replaced by the
  839. * original member acted on by the function or functions.
  840. */
  841. fluid.transform = function (source) {
  842. var togo = fluid.freshContainer(source);
  843. if (fluid.isArrayable(source)) {
  844. for (var i = 0; i < source.length; ++ i) {
  845. transformInternal(source, togo, i, arguments);
  846. }
  847. }
  848. else {
  849. for (var key in source) {
  850. transformInternal(source, togo, key, arguments);
  851. }
  852. }
  853. return togo;
  854. };
  855. /** Scan through a list of objects, terminating on and returning the first member which
  856. * matches a predicate function.
  857. * @param list {Array} The list of objects to be searched.
  858. * @param fn {Function} A predicate function, acting on a list member. A predicate which
  859. * returns any value which is not <code>null</code> or <code>undefined</code> will terminate
  860. * the search. The function accepts (object, index).
  861. * @param deflt {Object} A value to be returned in the case no predicate function matches
  862. * a list member. The default will be the natural value of <code>undefined</code>
  863. * @return The first return value from the predicate function which is not <code>null</code>
  864. * or <code>undefined</code>
  865. */
  866. fluid.find = function (list, fn, deflt) {
  867. for (var i = 0; i < list.length; ++ i) {
  868. var transit = fn(list[i], i);
  869. if (transit !== null && transit !== undefined) {
  870. return transit;
  871. }
  872. }
  873. return deflt;
  874. };
  875. /** Scan through a list of objects, "accumulating" a value over them
  876. * (may be a straightforward "sum" or some other chained computation).
  877. * @param list {Array} The list of objects to be accumulated over.
  878. * @param fn {Function} An "accumulation function" accepting the signature (object, total, index) where
  879. * object is the list member, total is the "running total" object (which is the return value from the previous function),
  880. * and index is the index number.
  881. * @param arg {Object} The initial value for the "running total" object.
  882. * @return {Object} the final running total object as returned from the final invocation of the function on the last list member.
  883. */
  884. fluid.accumulate = function (list, fn, arg) {
  885. for (var i = 0; i < list.length; ++ i) {
  886. arg = fn(list[i], arg, i);
  887. }
  888. return arg;
  889. };
  890. /** Can through a list of objects, removing those which match a predicate. Similar to
  891. * jQuery.grep, only acts on the list in-place by removal, rather than by creating
  892. * a new list by inclusion.
  893. * @param list {Array} The list of objects to be scanned over.
  894. * @param fn {Function} A predicate function determining whether an element should be
  895. * removed. This accepts the standard signature (object, index) and returns a "truthy"
  896. * result in order to determine that the supplied object should be removed from the list.
  897. * @return The list, transformed by the operation of removing the matched elements. The
  898. * supplied list is modified by this operation.
  899. */
  900. fluid.remove_if = function (list, fn) {
  901. for (var i = 0; i < list.length; ++ i) {
  902. if (fn(list[i], i)) {
  903. list.splice(i, 1);
  904. --i;
  905. }
  906. }
  907. return list;
  908. };
  909. // Other useful helpers.
  910. /**
  911. * Simple string template system.
  912. * Takes a template string containing tokens in the form of "%value".
  913. * Returns a new string with the tokens replaced by the specified values.
  914. * Keys and values can be of any data type that can be coerced into a string. Arrays will work here as well.
  915. *
  916. * @param {String} template a string (can be HTML) that contains tokens embedded into it
  917. * @param {object} values a collection of token keys and values
  918. */
  919. fluid.stringTemplate = function (template, values) {
  920. var newString = template;
  921. for (var key in values) {
  922. var searchStr = "%" + key;
  923. newString = newString.replace(searchStr, values[key]);
  924. }
  925. return newString;
  926. };
  927. })(jQuery, fluid_1_2);
  928. /*
  929. Copyright 2008-2010 University of Cambridge
  930. Copyright 2008-2009 University of Toronto
  931. Licensed under the Educational Community License (ECL), Version 2.0 or the New
  932. BSD license. You may not use this file except in compliance with one these
  933. Licenses.
  934. You may obtain a copy of the ECL 2.0 License and BSD License at
  935. https://source.fluidproject.org/svn/LICENSE.txt
  936. */
  937. // Declare dependencies.
  938. /*global jQuery */
  939. var fluid_1_2 = fluid_1_2 || {};
  940. (function ($, fluid) {
  941. fluid.dom = fluid.dom || {};
  942. // Node walker function for iterateDom.
  943. var getNextNode = function (iterator) {
  944. if (iterator.node.firstChild) {
  945. iterator.node = iterator.node.firstChild;
  946. iterator.depth += 1;
  947. return iterator;
  948. }
  949. while (iterator.node) {
  950. if (iterator.node.nextSibling) {
  951. iterator.node = iterator.node.nextSibling;
  952. return iterator;
  953. }
  954. iterator.node = iterator.node.parentNode;
  955. iterator.depth -= 1;
  956. }
  957. return iterator;
  958. };
  959. /**
  960. * Walks the DOM, applying the specified acceptor function to each element.
  961. * There is a special case for the acceptor, allowing for quick deletion of elements and their children.
  962. * Return "delete" from your acceptor function if you want to delete the element in question.
  963. * Return "stop" to terminate iteration.
  964. *
  965. * @param {Element} node the node to start walking from
  966. * @param {Function} acceptor the function to invoke with each DOM element
  967. * @param {Boolean} allnodes Use <code>true</code> to call acceptor on all nodes,
  968. * rather than just element nodes (type 1)
  969. */
  970. fluid.dom.iterateDom = function (node, acceptor, allNodes) {
  971. var currentNode = {node: node, depth: 0};
  972. var prevNode = node;
  973. var condition;
  974. while (currentNode.node !== null && currentNode.depth >= 0 && currentNode.depth < fluid.dom.iterateDom.DOM_BAIL_DEPTH) {
  975. condition = null;
  976. if (currentNode.node.nodeType === 1 || allNodes) {
  977. condition = acceptor(currentNode.node, currentNode.depth);
  978. }
  979. if (condition) {
  980. if (condition === "delete") {
  981. currentNode.node.parentNode.removeChild(currentNode.node);
  982. currentNode.node = prevNode;
  983. }
  984. else if (condition === "stop") {
  985. return currentNode.node;
  986. }
  987. }
  988. prevNode = currentNode.node;
  989. currentNode = getNextNode(currentNode);
  990. }
  991. };
  992. // Work around IE circular DOM issue. This is the default max DOM depth on IE.
  993. // http://msdn2.microsoft.com/en-us/library/ms761392(VS.85).aspx
  994. fluid.dom.iterateDom.DOM_BAIL_DEPTH = 256;
  995. /**
  996. * Checks if the sepcified container is actually the parent of containee.
  997. *
  998. * @param {Element} container the potential parent
  999. * @param {Element} containee the child in question
  1000. */
  1001. fluid.dom.isContainer = function (container, containee) {
  1002. for (; containee; containee = containee.parentNode) {
  1003. if (container === containee) {
  1004. return true;
  1005. }
  1006. }
  1007. return false;
  1008. };
  1009. /** Return the element text from the supplied DOM node as a single String */
  1010. fluid.dom.getElementText = function(element) {
  1011. var nodes = element.childNodes;
  1012. var text = "";
  1013. for (var i = 0; i < nodes.length; ++ i) {
  1014. var child = nodes[i];
  1015. if (child.nodeType == 3) {
  1016. text = text + child.nodeValue;
  1017. }
  1018. }
  1019. return text;
  1020. };
  1021. })(jQuery, fluid_1_2);
  1022. /*
  1023. Copyright 2008-2010 University of Cambridge
  1024. Copyright 2008-2009 University of Toronto
  1025. Licensed under the Educational Community License (ECL), Version 2.0 or the New
  1026. BSD license. You may not use this file except in compliance with one these
  1027. Licenses.
  1028. You may obtain a copy of the ECL 2.0 License and BSD License at
  1029. https://source.fluidproject.org/svn/LICENSE.txt
  1030. */
  1031. /*global jQuery*/
  1032. /*global fluid_1_2*/
  1033. fluid_1_2 = fluid_1_2 || {};
  1034. (function ($, fluid) {
  1035. var unUnicode = /(\\u[\dabcdef]{4}|\\x[\dabcdef]{2})/g;
  1036. fluid.unescapeProperties = function (string) {
  1037. string = string.replace(unUnicode, function(match) {
  1038. var code = match.substring(2);
  1039. var parsed = parseInt(code, 16);
  1040. return String.fromCharCode(parsed);
  1041. }
  1042. );
  1043. var pos = 0;
  1044. while (true) {
  1045. var backpos = string.indexOf("\\", pos);
  1046. if (backpos === -1) {
  1047. break;
  1048. }
  1049. if (backpos === string.length - 1) {
  1050. return [string.substring(0, string.length - 1), true];
  1051. }
  1052. var replace = string.charAt(backpos + 1);
  1053. if (replace === "n") replace = "\n";
  1054. if (replace === "r") replace = "\r";
  1055. if (replace === "t") replace = "\t";
  1056. string = string.substring(0, backpos) + replace + string.substring(backpos + 2);
  1057. pos = backpos + 1;
  1058. }
  1059. return [string, false];
  1060. };
  1061. var breakPos = /[^\\][\s:=]/;
  1062. fluid.parseJavaProperties = function(text) {
  1063. // File format described at http://java.sun.com/javase/6/docs/api/java/util/Properties.html#load(java.io.Reader)
  1064. var togo = {};
  1065. text = text.replace(/\r\n/g, "\n");
  1066. text = text.replace(/\r/g, "\n");
  1067. lines = text.split("\n");
  1068. var contin, key, valueComp, valueRaw, valueEsc;
  1069. for (var i = 0; i < lines.length; ++ i) {
  1070. var line = $.trim(lines[i]);
  1071. if (!line || line.charAt(0) === "#" || line.charAt(0) === '!') {
  1072. continue;
  1073. }
  1074. if (!contin) {
  1075. valueComp = "";
  1076. var breakpos = line.search(breakPos);
  1077. if (breakpos === -1) {
  1078. key = line;
  1079. valueRaw = "";
  1080. }
  1081. else {
  1082. key = $.trim(line.substring(0, breakpos + 1)); // +1 since first char is escape exclusion
  1083. valueRaw = $.trim(line.substring(breakpos + 2));
  1084. if (valueRaw.charAt(0) === ":" || valueRaw.charAt(0) === "=") {
  1085. valueRaw = $.trim(valueRaw.substring(1));
  1086. }
  1087. }
  1088. key = fluid.unescapeProperties(key)[0];
  1089. valueEsc = fluid.unescapeProperties(valueRaw);
  1090. }
  1091. else {
  1092. valueEsc = fluid.unescapeProperties(line);
  1093. }
  1094. contin = valueEsc[1];
  1095. if (!valueEsc[1]) { // this line was not a continuation line - store the value
  1096. togo[key] = valueComp + valueEsc[0];
  1097. }
  1098. else {
  1099. valueComp += valueEsc[0];
  1100. }
  1101. }
  1102. return togo;
  1103. };
  1104. /**
  1105. * Expand a message string with respect to a set of arguments, following a basic
  1106. * subset of the Java MessageFormat rules.
  1107. * http://java.sun.com/j2se/1.4.2/docs/api/java/text/MessageFormat.html
  1108. *
  1109. * The message string is expected to contain replacement specifications such
  1110. * as {0}, {1}, {2}, etc.
  1111. * @param messageString {String} The message key to be expanded
  1112. * @param args {String/Array of String} An array of arguments to be substituted into the message.
  1113. * @return The expanded message string.
  1114. */
  1115. fluid.formatMessage = function (messageString, args) {
  1116. if (!args) {
  1117. return messageString;
  1118. }
  1119. if (typeof(args) === "string") {
  1120. args = [args];
  1121. }
  1122. for (var i = 0; i < args.length; ++ i) {
  1123. messageString = messageString.replace("{" + i + "}", args[i]);
  1124. }
  1125. return messageString;
  1126. };
  1127. })(jQuery, fluid_1_2);
  1128. /*
  1129. Copyright 2007-2010 University of Cambridge
  1130. Copyright 2007-2009 University of Toronto
  1131. Copyright 2007-2009 University of California, Berkeley
  1132. Licensed under the Educational Community License (ECL), Version 2.0 or the New
  1133. BSD license. You may not use this file except in compliance with one these
  1134. Licenses.
  1135. You may obtain a copy of the ECL 2.0 License and BSD License at
  1136. https://source.fluidproject.org/svn/LICENSE.txt
  1137. */
  1138. // Declare dependencies.
  1139. /*global jQuery, YAHOO, opera*/
  1140. var fluid_1_2 = fluid_1_2 || {};
  1141. var fluid = fluid || fluid_1_2;
  1142. (function ($, fluid) {
  1143. fluid.renderTimestamp = function (date) {
  1144. var zeropad = function (num, width) {
  1145. if (!width) width = 2;
  1146. var numstr = (num == undefined? "" : num.toString());
  1147. return "00000".substring(5 - width + numstr.length) + numstr;
  1148. }
  1149. return zeropad(date.getHours()) + ":" + zeropad(date.getMinutes()) + ":" + zeropad(date.getSeconds()) + "." + zeropad(date.getMilliseconds(), 3);
  1150. };
  1151. /**
  1152. * Dumps a DOM element into a readily recognisable form for debugging - produces a
  1153. * "semi-selector" summarising its tag name, class and id, whichever are set.
  1154. *
  1155. * @param {jQueryable} element The element to be dumped
  1156. * @return A string representing the element.
  1157. */
  1158. fluid.dumpEl = function (element) {
  1159. var togo;
  1160. if (!element) {
  1161. return "nu…

Large files files are truncated, but you can click here to view the full file