jsMotif.js 56 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763
  1. /** This file is part of jsMotif
  2. Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).*
  3. All rights reserved.
  4. Contact: Nokia Corporation marek.krysiuk@nokia.com
  5. You may use this file under the terms of the BSD license as follows:
  6. Redistribution and use in source and binary forms, with or without modification,
  7. are permitted provided that the following conditions are met:
  8. * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  9. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
  10. * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
  11. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  12. */
  13. var jsMotif = jsMotif || {};/**
  14. * jsMotif cross-browser dom enabler for document manipulation and events used by <code>{@link jsMotif.Template}</code>.
  15. *
  16. * @namespace
  17. * @publish
  18. */
  19. jsMotif.dom = (function () {
  20. /* Local reference to document, because accessing a local variable is faster then a global one */
  21. var _D = document;
  22. function byId (id) {
  23. return _D.getElementById(id);
  24. }
  25. /**
  26. * Always returns a DOM element - if an id is passed as a parameter it lookups an element
  27. * with that id otherwise it returns the element passed as the parameter. This is a helper function
  28. * for writing functions that accept both element ids and element objects.
  29. *
  30. * @private
  31. * @param {Object} el a DOM element or a element's id
  32. * @param {Object} a DOM element or null if the requested element wasn't found
  33. */
  34. function _elem (el) {
  35. return typeof el === 'string' ? byId(el) : el;
  36. }
  37. function byTagName (tagName,/*?*/ startElem) {
  38. return (startElem || _D).getElementsByTagName(tagName);
  39. }
  40. function extendNodeAttributes (node, attributes) {
  41. var attr;
  42. for (attr in attributes) {
  43. if (attributes.hasOwnProperty(attr)) {
  44. if (attr === 'class') {
  45. node.className = attributes[attr];
  46. }else{
  47. node[attr] = attributes[attr];
  48. }
  49. }
  50. }
  51. return node;
  52. }
  53. function remove (elem) {
  54. var node = _elem(elem);
  55. if(node && node.parentNode){
  56. node.parentNode.removeChild(node);
  57. }
  58. }
  59. function create(tag, /*?*/params, /*?*/body) {
  60. var node = _D.createElement(tag);
  61. node = extendNodeAttributes(node, params);
  62. if (body && typeof body === 'object' && body.appendChild) {
  63. node.appendChild(body);
  64. } else if (typeof body === 'string') {
  65. node.appendChild(_D.createTextNode(body));
  66. }
  67. return node;
  68. }
  69. function createFragment () {
  70. return _D.createDocumentFragment();
  71. }
  72. function append (elem, childNode) {
  73. var parentNode = _elem(elem);
  74. if (parentNode) {
  75. parentNode.appendChild(childNode);
  76. }
  77. }
  78. /**
  79. * Moves childs from one node to another
  80. * @param {Object} target node
  81. * @param {Object} src node
  82. */
  83. function moveChildNodes (target, src) {
  84. var node = src.firstChild;
  85. while(node){
  86. append(target, node);
  87. node = src.firstChild;
  88. }
  89. }
  90. /**
  91. * Function stores a event handler for the event so all handlers can be removed later on
  92. *
  93. * @private
  94. * @param {Object} node a dom element which have an event added
  95. * @param {String} event event name
  96. * @param {Function} handler event handler
  97. */
  98. function _saveHandler (node, event, handler) {
  99. var path = '_' + event + 'Handlers';
  100. if (node) {
  101. node[path] = node[path] || [];
  102. node[path].push(handler);
  103. }
  104. return true;
  105. }
  106. function getMouseRelatedTarget (event) {
  107. event = event || window.event;
  108. var relTarget = event.relatedTarget;
  109. if (!relTarget) {
  110. if (event.type === "mouseout") {
  111. relTarget = event.toElement;
  112. } else if (event.type === "mouseover") {
  113. relTarget = event.fromElement;
  114. }
  115. }
  116. return relTarget;
  117. }
  118. function isContainedWithin (parent, child) {
  119. while (child && child !== parent) {
  120. child = child.parentNode;
  121. }
  122. return child === parent;
  123. }
  124. function _emulateMouseEnterLeave (element, handler) {
  125. return function (e) {
  126. var target = getMouseRelatedTarget(e);
  127. if (!isContainedWithin(element, target)) {
  128. handler(e);
  129. }
  130. };
  131. }
  132. function addEvent (elem, event, handler, capture) {
  133. var node = _elem(elem),
  134. fn;
  135. if (!node) {
  136. return;
  137. }
  138. if (node.addEventListener) {
  139. fn = function (elem, event, handler, capture) {
  140. var node = _elem(elem);
  141. if (node) {
  142. var isCaptured = typeof capture !== 'undefined' ? capture : false;
  143. if (event === 'mouseenter' || event === 'mouseleave') {
  144. var nativeEvent = event === 'mouseenter' ? 'mouseover' : 'mouseout';
  145. handler = _emulateMouseEnterLeave(node, handler);
  146. node.addEventListener(nativeEvent, handler, isCaptured);
  147. _saveHandler(node, event, handler);
  148. } else {
  149. _saveHandler(node, event, handler);
  150. node.addEventListener(event, handler, isCaptured);
  151. //FF registers the mouse wheel event in a different way than the rest
  152. if (event === 'mousewheel') {
  153. node.addEventListener("DOMMouseScroll", handler, isCaptured);
  154. }
  155. }
  156. }
  157. return handler;
  158. };
  159. } else if (node.attachEvent) {
  160. fn = function (elem, event, handler) {
  161. var node = _elem(elem);
  162. if(node){
  163. var newHandler = function() {
  164. handler.call(elem, window.event, arguments);
  165. };
  166. _saveHandler(node,event,newHandler);
  167. node.attachEvent('on' + event, newHandler);
  168. return newHandler;
  169. }
  170. };
  171. }
  172. if (fn) {
  173. jsMotif.dom.addEvent = fn;
  174. return fn(node, event, handler, capture);
  175. }
  176. }
  177. function hide () {
  178. var node,
  179. dispStyle,
  180. alen = arguments.length;
  181. while (alen--) {
  182. node = _elem(arguments[alen]);
  183. if (node) {
  184. dispStyle = node.style.display;
  185. node._lastDisplay = (dispStyle !== "none") ? dispStyle : "";
  186. node.style.display = "none";
  187. }
  188. }
  189. }
  190. function show () {
  191. var node,
  192. alen = arguments.length;
  193. while (alen--) {
  194. node = _elem(arguments[alen]);
  195. if (node) {
  196. node.style.display = node._lastDisplay || "";
  197. }
  198. }
  199. }
  200. function stopPropagation (e) {
  201. var event = e || window.event;
  202. event.cancelBubble = true;
  203. if(event.stopPropagation){
  204. event.stopPropagation();
  205. }
  206. }
  207. function getEventSource (e) {
  208. var event = e || window.event;
  209. return event.target || event.srcElement;
  210. }
  211. function preventDefault (e) {
  212. var event = e || window.event;
  213. event.returnValue = false;
  214. if (event.preventDefault) {
  215. event.preventDefault();
  216. }
  217. }
  218. /** @lends jsMotif.dom*/
  219. return {
  220. /**
  221. * Retrieves the DOM element with the given id.
  222. *
  223. * @function
  224. * @publish
  225. *
  226. * @param {String} id id of the requested element
  227. * @return {Object} a DOM node or null if the requested element wasn't found
  228. */
  229. byId: byId,
  230. /**
  231. * Creates a DOM element object based on input parameters.
  232. *
  233. * @function
  234. * @publish
  235. *
  236. * @param tag {String} tag name of the element
  237. * @param params {Object} a javascript object with properties which describe element attributes; optional
  238. * @param body {String|Object} a body of the element (text or DOM element); optional
  239. * @return {Object} created element
  240. */
  241. create: create,
  242. /**
  243. * Creates a document fragment.
  244. *
  245. * @function
  246. * @publish
  247. *
  248. * @return {Object} created DOM fragment
  249. */
  250. createFragment: createFragment,
  251. /**
  252. * Appends given node(s) to a parent. Can be easly combined with create function. This is a
  253. * variable-length argument list function.
  254. *
  255. * @function
  256. * @publish
  257. *
  258. * @param {String|Object} elem parent node object or its id
  259. * @param {Object} childNode-s an element/elements to be added in the same order as provided to arguments
  260. */
  261. append: append,
  262. /**
  263. * Registers a new event listener. Returned handler may be different than the passed one (event emulation).
  264. * Remember to use the returned handler for event removal. Supported event names are dom events without 'on' prefix
  265. * i.e <code>mouseup</code>, <code>click</code>. Plus <code>mouseenter</code> and <code>mouseleave</code> events emulation.
  266. *
  267. * @function
  268. * @publish
  269. *
  270. * @param {String|Object} elem a DOM element or its id
  271. * @param {String} event lower-case event name without 'on' prefix
  272. * @param {Function} handler handler function
  273. * @param {Boolean} capture default: false
  274. * @return event handler, may be different than the passed one if event emulation is needed
  275. */
  276. addEvent: addEvent,
  277. /**
  278. * Hides an element. This is a variable-length argument list function.
  279. *
  280. * @function
  281. * @publish
  282. *
  283. * @param {String|Object} elem-s one or more DOM elements or their ids
  284. */
  285. hide: hide,
  286. /**
  287. * Shows the hidden element. This is a variable-length argument list function.
  288. *
  289. * @function
  290. * @publish
  291. *
  292. * @param {String|Object} elem-s one or more DOM elements or their ids
  293. */
  294. show: show,
  295. /**
  296. * Stops propagation of an event, abstract layer for handling stopping propagation in IE
  297. * and other browsers because they handle it differently.
  298. *
  299. * @function
  300. * @publish
  301. *
  302. * @param {Object} e an event object
  303. */
  304. stopPropagation: stopPropagation,
  305. /**
  306. * Extends the given node with the given attributes.
  307. *
  308. * @function
  309. * @publish
  310. *
  311. * @param {Object} node a DOM node
  312. * @param {Object} attributes a javascript object containing attributes map
  313. * @return {Object} extended object
  314. */
  315. extendNodeAttributes: extendNodeAttributes,
  316. /**
  317. * Checks if the child parameter is a child of the parent parameter in the DOM or if the
  318. * child parameter is equal to the parent parameter.
  319. *
  320. * @function
  321. * @publish
  322. *
  323. * @param {Object} parent a DOM object
  324. * @param {Object} child a DOM object
  325. * @return {Boolean} true if the child parameter is a child of the parent parameter
  326. */
  327. isContainedWithin: isContainedWithin,
  328. /**
  329. * Cross browser method for retrieving relatedTarget property of a mouse event
  330. * as returned by FF.
  331. *
  332. * @function
  333. * @publish
  334. *
  335. * @param {Object} event a mouse event to retrieve the relatedTarget from
  336. * @return {Object} relatedTarget property of the event parameter
  337. */
  338. getMouseRelatedTarget: getMouseRelatedTarget,
  339. /**
  340. * Cross browser method for retrieving event source element.
  341. *
  342. * @function
  343. * @publish
  344. *
  345. * @param {Object} event an event object to retrieve source element from
  346. * @return {Object} source element property of the event
  347. */
  348. getEventSource: getEventSource,
  349. /**
  350. * Prevents default action of an event, abstract layer for preventing default actions in IE
  351. * and other browsers because they handle it differently.
  352. *
  353. * @function
  354. * @publish
  355. *
  356. * @param {Object} e an event object
  357. */
  358. preventDefault: preventDefault,
  359. /**
  360. * Function checks and returns element. If dom element is provided then
  361. * it returns the element if string provided it tries to fetch element by id.
  362. * @function
  363. * @publish
  364. *
  365. * @param {String|Object} element id of dome element or element reference
  366. */
  367. elem: _elem,
  368. /**
  369. * Removes Node from DOM
  370. * @function
  371. * @publish
  372. *
  373. * @param {Object} dom node
  374. */
  375. remove: remove,
  376. moveChildNodes: moveChildNodes
  377. };
  378. }());
  379. jsMotif.comm = {};
  380. jsMotif.comm.jsonParse = (function () {
  381. "use strict";
  382. // This is a function that can parse a JSON text, producing a JavaScript
  383. // data structure. It is a simple, recursive descent parser. It does not use
  384. // eval or regular expressions, so it can be used as a model for implementing
  385. // a JSON parser in other languages.
  386. // We are defining the function inside of another function to avoid creating
  387. // global variables.
  388. var at, // The index of the current character
  389. ch, // The current character
  390. escapee = {
  391. '"': '"',
  392. '\\': '\\',
  393. '/': '/',
  394. b: '\b',
  395. f: '\f',
  396. n: '\n',
  397. r: '\r',
  398. t: '\t'
  399. },
  400. text,
  401. error = function (m) {
  402. // Call error when something is wrong.
  403. throw {
  404. name: 'SyntaxError',
  405. message: m,
  406. at: at,
  407. text: text
  408. };
  409. },
  410. next = function (c) {
  411. // If a c parameter is provided, verify that it matches the current character.
  412. if (c && c !== ch) {
  413. error("Expected '" + c + "' instead of '" + ch + "'");
  414. }
  415. // Get the next character. When there are no more characters,
  416. // return the empty string.
  417. ch = text.charAt(at);
  418. at += 1;
  419. return ch;
  420. },
  421. number = function () {
  422. // Parse a number value.
  423. var number,
  424. string = '';
  425. if (ch === '-') {
  426. string = '-';
  427. next('-');
  428. }
  429. while (ch >= '0' && ch <= '9') {
  430. string += ch;
  431. next();
  432. }
  433. if (ch === '.') {
  434. string += '.';
  435. while (next() && ch >= '0' && ch <= '9') {
  436. string += ch;
  437. }
  438. }
  439. if (ch === 'e' || ch === 'E') {
  440. string += ch;
  441. next();
  442. if (ch === '-' || ch === '+') {
  443. string += ch;
  444. next();
  445. }
  446. while (ch >= '0' && ch <= '9') {
  447. string += ch;
  448. next();
  449. }
  450. }
  451. number = +string;
  452. if (!isFinite(number)) {
  453. error("Bad number");
  454. } else {
  455. return number;
  456. }
  457. },
  458. string = function () {
  459. // Parse a string value.
  460. var hex,
  461. i,
  462. string = '',
  463. uffff;
  464. // When parsing for string values, we must look for " and \ characters.
  465. if (ch === '"') {
  466. while (next()) {
  467. if (ch === '"') {
  468. next();
  469. return string;
  470. } else if (ch === '\\') {
  471. next();
  472. if (ch === 'u') {
  473. uffff = 0;
  474. for (i = 0; i < 4; i += 1) {
  475. hex = parseInt(next(), 16);
  476. if (!isFinite(hex)) {
  477. break;
  478. }
  479. uffff = uffff * 16 + hex;
  480. }
  481. string += String.fromCharCode(uffff);
  482. } else if (typeof escapee[ch] === 'string') {
  483. string += escapee[ch];
  484. } else {
  485. break;
  486. }
  487. } else {
  488. string += ch;
  489. }
  490. }
  491. }
  492. error("Bad string");
  493. },
  494. white = function () {
  495. // Skip whitespace.
  496. while (ch && ch <= ' ') {
  497. next();
  498. }
  499. },
  500. word = function () {
  501. // true, false, or null.
  502. switch (ch) {
  503. case 't':
  504. next('t');
  505. next('r');
  506. next('u');
  507. next('e');
  508. return true;
  509. case 'f':
  510. next('f');
  511. next('a');
  512. next('l');
  513. next('s');
  514. next('e');
  515. return false;
  516. case 'n':
  517. next('n');
  518. next('u');
  519. next('l');
  520. next('l');
  521. return null;
  522. }
  523. error("Unexpected '" + ch + "'");
  524. },
  525. value, // Place holder for the value function.
  526. array = function () {
  527. // Parse an array value.
  528. var array = [];
  529. if (ch === '[') {
  530. next('[');
  531. white();
  532. if (ch === ']') {
  533. next(']');
  534. return array; // empty array
  535. }
  536. while (ch) {
  537. array.push(value());
  538. white();
  539. if (ch === ']') {
  540. next(']');
  541. return array;
  542. }
  543. next(',');
  544. white();
  545. }
  546. }
  547. error("Bad array");
  548. },
  549. object = function () {
  550. // Parse an object value.
  551. var key,
  552. object = {};
  553. if (ch === '{') {
  554. next('{');
  555. white();
  556. if (ch === '}') {
  557. next('}');
  558. return object; // empty object
  559. }
  560. while (ch) {
  561. key = string();
  562. white();
  563. next(':');
  564. if (Object.hasOwnProperty.call(object, key)) {
  565. error('Duplicate key "' + key + '"');
  566. }
  567. object[key] = value();
  568. white();
  569. if (ch === '}') {
  570. next('}');
  571. return object;
  572. }
  573. next(',');
  574. white();
  575. }
  576. }
  577. error("Bad object");
  578. };
  579. value = function () {
  580. // Parse a JSON value. It could be an object, an array, a string, a number,
  581. // or a word.
  582. white();
  583. switch (ch) {
  584. case '{':
  585. return object();
  586. case '[':
  587. return array();
  588. case '"':
  589. return string();
  590. case '-':
  591. return number();
  592. default:
  593. return ch >= '0' && ch <= '9' ? number() : word();
  594. }
  595. };
  596. // Return the json_parse function. It will have access to all of the above
  597. // functions and variables.
  598. return function (source, reviver) {
  599. var result;
  600. text = source;
  601. at = 0;
  602. ch = ' ';
  603. result = value();
  604. white();
  605. if (ch) {
  606. error("Syntax error");
  607. }
  608. // If there is a reviver function, we recursively walk the new structure,
  609. // passing each name/value pair to the reviver function for possible
  610. // transformation, starting with a temporary root object that holds the result
  611. // in an empty key. If there is not a reviver function, we simply return the
  612. // result.
  613. return typeof reviver === 'function' ? (function walk(holder, key) {
  614. var k, v, value = holder[key];
  615. if (value && typeof value === 'object') {
  616. for (k in value) {
  617. if (Object.prototype.hasOwnProperty.call(value, k)) {
  618. v = walk(value, k);
  619. if (v !== undefined) {
  620. value[k] = v;
  621. } else {
  622. delete value[k];
  623. }
  624. }
  625. }
  626. }
  627. return reviver.call(holder, key, value);
  628. }({'': result}, '')) : result;
  629. };
  630. }());
  631. jsMotif.comm.xhttp = (function(){
  632. var jsonParse = (window.JSON)? window.JSON.parse : jsMotif.comm.jsonParse;
  633. function createXmlHttpObject() {
  634. if(window.XMLHttpRequest){
  635. return new XMLHttpRequest();
  636. }else{
  637. return new ActiveXObject("Microsoft.XMLHTTP");
  638. }
  639. }
  640. function getJson (url, onComplete, params) {
  641. params = params || {};
  642. var xmlHttp = createXmlHttpObject(),
  643. headers = params.headers || {},
  644. timeout = params.timeout || 4000;
  645. xmlHttp.timeout = timeout;
  646. xmlHttp.open('GET', url);
  647. xmlHttp.setRequestHeader('Accept','application/json');
  648. xmlHttp.setRequestHeader('Content-Type','application/json');
  649. if(headers){
  650. for(var key in headers){
  651. if(headers.hasOwnProperty(key)){
  652. xmlHttp.setRequestHeader(key,headers[key]);
  653. }
  654. }
  655. }
  656. xmlHttp.ontimeout = function(){
  657. onComplete({}, 'timeout');
  658. };
  659. xmlHttp.onreadystatechange = function(){
  660. if(xmlHttp.readyState === 4){
  661. var respObject = {},
  662. status = xmlHttp.status,
  663. respText = xmlHttp.responseText;
  664. if(status === 200){
  665. respObject = jsonParse(respText);
  666. }
  667. onComplete(respObject, (status === 200)? 'ok' : 'error');
  668. }
  669. };
  670. xmlHttp.send();
  671. }
  672. return {
  673. getJson: getJson
  674. };
  675. }());
  676. jsMotif.comm.jsonp = (function(){
  677. var _dom = jsMotif.dom,
  678. _seed = 100,
  679. _cbfnNS= 'jsMotif.comm.jsonp',
  680. _defaultTimeout = 4000;
  681. function getCbId(){
  682. return ++_seed;
  683. }
  684. function get (url) {
  685. var script = _dom.create('script', {
  686. src: url,
  687. type: 'text/javascript'
  688. });
  689. script.onload = function(){
  690. _dom.remove(this);
  691. };
  692. //ie hack
  693. script.onreadystatechange = function () {
  694. if (this.readyState === 'loaded') {
  695. _dom.remove(this);
  696. }
  697. };
  698. _dom.append(document.body, script);
  699. }
  700. function removeCallBack(cbName){
  701. delete jsMotif.comm.jsonp[cbName];
  702. }
  703. function createHandler (onComplete, timeout){
  704. var $comm = jsMotif.comm,
  705. cbId = getCbId(),
  706. handlerName = 'handle' + cbId,
  707. timeoutHandler = setTimeout(function(){
  708. onComplete({}, 'timeout');
  709. $comm.jsonp[handlerName] = function(){};
  710. //add 5 sec and remove callback if request eventually will come in
  711. setTimeout(function(){
  712. removeCallBack(handlerName);
  713. },5000);
  714. }, timeout || _defaultTimeout);
  715. $comm.jsonp[handlerName] = function (data) {
  716. clearTimeout(timeoutHandler);
  717. onComplete(data, 'ok');
  718. removeCallBack(handlerName);
  719. };
  720. return handlerName;
  721. }
  722. function getJson (url, onComplete, params) {
  723. var timeout = (params)? params.timeout : '',
  724. handlerName = createHandler(onComplete, timeout),
  725. requestUrl = url.replace('{CB_FN}', [_cbfnNS , handlerName].join('.'));
  726. get(requestUrl);
  727. }
  728. return {
  729. getJson: getJson
  730. };
  731. }());
  732. /**
  733. * Template errors and warning definitions
  734. * @namespace
  735. * @publish
  736. */
  737. jsMotif.errors = {
  738. LVL: {
  739. log: 3,
  740. warn: 2,
  741. critical: 1
  742. },
  743. MSG: {
  744. undefData: 'Undefined data',
  745. undefModule: 'Undefined module',
  746. wrongSrcNode: 'This node type cannot have src attribute',
  747. reqRelNotFound: 'Cannot found required rel %s1 in %s2 module'
  748. }
  749. };
  750. /**
  751. * This namespace defines selector for JSON objects, which is a simplified Xpath for
  752. * safe JSON object data selection. The functions in this namesspace are used within
  753. * <code>{@link jsMotif.Template}</code>.
  754. * @namespace
  755. * @publish
  756. */
  757. jsMotif.selector = (function(){
  758. /**
  759. * Trim
  760. * @private
  761. * @param {Object} text
  762. */
  763. function trim (text) {
  764. return text.replace(/(^\s+)|(\s+$)/g, '');
  765. }
  766. /**
  767. * Unquote
  768. * @param {Object} text
  769. */
  770. function unquote(text){
  771. return text.replace(/(^\')|(^\")|(\'$)|(\"$)/g, '');
  772. }
  773. /**
  774. * Filtering operatos
  775. *
  776. * @private
  777. * @param {Object} argLeft
  778. * @param {Object} argRight
  779. */
  780. var operators = {
  781. '==': function(argLeft, argRight){
  782. return argLeft == argRight;
  783. },
  784. '!=': function (argLeft, argRight){
  785. return (argLeft)? argLeft != argRight : false;
  786. },
  787. '>': function (argLeft, argRight){
  788. return argLeft > argRight;
  789. },
  790. '<': function (argLeft, argRight){
  791. return argLeft < argRight;
  792. },
  793. '>=': function (argLeft, argRight){
  794. return argLeft >= argRight;
  795. },
  796. '<=': function (argLeft, argRight){
  797. return argLeft <= argRight;
  798. },
  799. '!': function (argLeft, argRight){
  800. return !argLeft;
  801. }
  802. };
  803. /**
  804. * Function parses filter selector, to set the action
  805. * function for the selector operator
  806. *
  807. * @private
  808. * @param {Object} selector
  809. */
  810. function parseSelector(selector) {
  811. var selectedIndex = -1,
  812. selectedOperator,
  813. index,
  814. argLeft,
  815. argRight,
  816. action;
  817. if(selector[0] === '!'){
  818. action = operators['!'];
  819. argLeft = trim(selector.substr(1, selector.length));
  820. }else{
  821. for(var operator in operators){
  822. if (operators.hasOwnProperty(operator)) {
  823. index = selector.lastIndexOf(operator);
  824. if (index > selectedIndex) {
  825. selectedIndex = index;
  826. selectedOperator = operator;
  827. }
  828. }
  829. }
  830. if (selectedIndex !== -1) {
  831. action = operators[selectedOperator];
  832. argRight = unquote(trim(selector.substr(selectedIndex, selector.length).split(selectedOperator)[1]));
  833. argLeft = (selectedOperator !== '!')? trim(selector.substr(0, selectedIndex)) : argRight;
  834. }
  835. }
  836. return {
  837. valuePath: argLeft,
  838. toValue: argRight,
  839. action: action
  840. };
  841. }
  842. /**
  843. * Function gets property for an object,
  844. * property is stored as an array of characters
  845. *
  846. * @private
  847. * @param {Object} obj
  848. * @param {Object} subPath
  849. */
  850. function getProperty(obj, subPath) {
  851. var property = subPath.join('');
  852. if(property!==''){
  853. obj = obj[property];
  854. }
  855. return obj;
  856. }
  857. /**
  858. * Functions for json object path tokens
  859. * handling. On each token action is performed
  860. *
  861. * @private
  862. * @param {Object} obj
  863. * @param {Object} subPath
  864. * @param {Object} stack
  865. * @param {Object} token
  866. */
  867. var tokens = {
  868. '.': function(obj, subPath, stack, token){
  869. if (!stack.length) {
  870. if (obj instanceof Array) {
  871. for(var i=0, l=obj.length, result = []; i<l; i++){
  872. result.push(getProperty(obj[i],subPath));
  873. }
  874. obj = result;
  875. }
  876. else {
  877. obj = getProperty(obj, subPath);
  878. }
  879. subPath.splice(0, subPath.length);
  880. }else{
  881. subPath.push(token);
  882. }
  883. return obj;
  884. },
  885. '(': function (obj, subPath){
  886. return obj;
  887. },
  888. ')': function (obj, subPath, stack, token) {
  889. if (!stack.length) {
  890. var property = subPath.join('');
  891. if (typeof obj[property] === 'function') {
  892. return obj[property]();
  893. }
  894. return;
  895. }else{
  896. subPath.push(token);
  897. }
  898. return obj;
  899. },
  900. '[': function (obj, subPath, stack, token){
  901. if(!stack.length){
  902. obj = getProperty(obj, subPath);
  903. subPath.splice(0, subPath.length);
  904. }else{
  905. subPath.push(token);
  906. }
  907. stack.push(token);
  908. return obj;
  909. },
  910. ']': function (obj, subPath, stack, token) {
  911. stack.pop();
  912. if(!stack.length){
  913. var property = subPath.join('');
  914. if(isNaN(property)){
  915. obj = getBySelector(obj, property);
  916. }else{
  917. if (property !== '') {
  918. obj = obj[parseInt(property, 10)];
  919. }
  920. }
  921. subPath.splice(0, subPath.length);
  922. }else{
  923. subPath.push(token);
  924. }
  925. return obj;
  926. }
  927. };
  928. /**
  929. * Normalize path, normalizes path removes extra '.', and '"' and adds
  930. * '.' at the end of the path
  931. * @private
  932. * @param {Object} path
  933. */
  934. function normalizePath (path) {
  935. path = (typeof(path) === 'string')? path : '';
  936. path = path.replace(/([\]\)\.]+)\./g, function($0, $1){
  937. return $1 ? $1 + '' : $0;
  938. });
  939. if(!tokens[path.charAt(path.length-1)]){
  940. path += '.';
  941. }
  942. return path;
  943. }
  944. /**
  945. * This function retrieves data from a JSON object, using the provided path.
  946. *
  947. * @param {Object} path The path to data within the JSON object; may contain array selectors
  948. * @param {Object} defaultValue The default value to return when the path finds nothing
  949. *
  950. * @publish
  951. *
  952. * @example
  953. * getData.call(jsonObject,'path1.path2[3]','default text');
  954. *
  955. */
  956. function getData(path, defaultValue) {
  957. var obj = this,
  958. subPath = [],
  959. stack = [];
  960. path = normalizePath(path);
  961. for(var i=0, l=path.length; i<l; i++){
  962. var ch = path.charAt(i);
  963. if(tokens[ch]){
  964. obj = tokens[ch](obj, subPath, stack, ch);
  965. }else{
  966. subPath.push(ch);
  967. }
  968. if(!obj){
  969. return defaultValue;
  970. }
  971. }
  972. return obj;
  973. }
  974. /**
  975. * This function resolves a conditional path or a selector.
  976. *
  977. * @param {Object} selector A selector or a conditional path ({String})
  978. * @return A value (data) selected by the argument or, if the argument contains a conditional
  979. * path, a Boolean value indicating if the condition is met
  980. * @publish
  981. *
  982. * @example
  983. * resolveCondition.call(jsonObj,'path1.inner1');
  984. * resolveCondition.call(jsonObj,'path1.inner2=="some text");
  985. */
  986. function resolveCondition (selector){
  987. var obj = this,
  988. select,
  989. value,
  990. data = getData.call(obj, selector, false);
  991. if(data){
  992. return data;
  993. }else{
  994. select = parseSelector(selector);
  995. if(select.action){
  996. value = getData.call(obj, select.valuePath);
  997. return select.action(value, select.toValue);
  998. }
  999. }
  1000. }
  1001. /**
  1002. * Function gets elements from the array according to
  1003. * selector
  1004. *
  1005. * @private
  1006. * @param {Object} obj
  1007. * @param {Object} selector
  1008. */
  1009. function getBySelector (obj, selector) {
  1010. var select = parseSelector(selector),
  1011. results = [];
  1012. for(var i=0, l=obj.length; i<l; i++){
  1013. var arrayObj = obj[i],
  1014. value = getData.call(arrayObj, select.valuePath);
  1015. if(select.action(value, select.toValue)){
  1016. results.push(arrayObj);
  1017. }
  1018. }
  1019. return results;
  1020. }
  1021. return {
  1022. resolveCondition: resolveCondition,
  1023. getData: getData
  1024. };
  1025. }());
  1026. /**
  1027. * <code>jsMotif</code> is a template rendering class extended by {@link nokia.places.APITemplate}.
  1028. * This method creates a new instance of <code>jsMotif</code>.
  1029. * @class
  1030. *
  1031. * @publish
  1032. * @param {Object} params An initialization object with the following properties
  1033. * <ul>
  1034. * <li>
  1035. * <code>template</code> - (mandatory) a value which represents a template it can be:
  1036. * <ul>
  1037. * <li>a {String} containing HTML elements, e.g '&lt;div>&lt;a>&lt;/a>&lt;/div>'</li>
  1038. * <li>a {String} containing an existing DOM element id (the template pattern is
  1039. * extracted from the DOM node</li>
  1040. * <li>a {Object} reference to existing DOM node in javascript</li>
  1041. * </ul>
  1042. * </li>
  1043. * <li>
  1044. * <code>targetNode</code> - the element into which the template is to be rendered; its value
  1045. * can be:
  1046. * <ul>
  1047. * <li>a {String} which represents an existing element id</li>
  1048. * <li>an {Object} which represents a reference to an existing DOM element in JavaScript</li>
  1049. * </ul>
  1050. * </li>
  1051. * <li>
  1052. * <code>functions</code> - an object/hashmap which contains a user-defined function that
  1053. * may be used to render or to affect rendering of the element in template.
  1054. * </li>
  1055. * <li>
  1056. * <code>event</code> - an array of objects which specify the events attached to template nodes and
  1057. * the event handlers; each object in the array has the following attributes:
  1058. * <ul>
  1059. * <li>
  1060. * <code>rel</code> - a {String} indicating the template node to which to attach the event
  1061. * </li>
  1062. * <li>
  1063. * <code>name</code> - a {String} providing the name of the attached event; the supported
  1064. * event names are 'keydown', 'keyup', 'keypress', 'click', 'dblclick', 'mousedown',
  1065. * 'mousemove', 'mouseout', 'mouseover', 'mouseup', 'mouseenter', 'mouseleave', 'blur',
  1066. * 'change', 'focus', 'reset', 'select', 'submit', 'abort'
  1067. * </li>
  1068. * <li>
  1069. * <code>handler</code> - a caller-defined {Function} to be invoked when the user
  1070. * triggers the attached event; the function receives the data object as an argument and
  1071. * 'this' within the function refers to the element on which the event was triggered
  1072. * </li>
  1073. * </ul>
  1074. * </li>
  1075. * </ul>
  1076. *
  1077. * @memberOf jsMotif
  1078. */
  1079. jsMotif.Template = (function(){
  1080. var $E = jsMotif.errors,
  1081. _dom = jsMotif.dom,
  1082. _selector = jsMotif.selector;
  1083. var _stripBrackets = function (value) {
  1084. if (value && '{' === value.charAt(0) && '}' === value.charAt(value.length-1)) {
  1085. return value.substr(1).substr(0, value.length-2);
  1086. }
  1087. return false;
  1088. };
  1089. /**
  1090. * Function returns a function path within json object
  1091. * @param {Object} path
  1092. *
  1093. * @private
  1094. */
  1095. var getFunctionPath = function (path){
  1096. var functionMatch = path.match(/(\S+)\(\)/);
  1097. if(functionMatch){
  1098. return functionMatch[1];
  1099. }
  1100. };
  1101. /**
  1102. * Filling target node with template
  1103. * @param {Object} node
  1104. * @param {Object} jsonObject
  1105. * @param {Object} fragment
  1106. *
  1107. * @private
  1108. */
  1109. var fillNode = function(node, jsonObject, fragment){
  1110. var cloned = fragment.cloneNode(true);
  1111. this.renderObject(cloned, jsonObject);
  1112. _dom.append(node, cloned);
  1113. };
  1114. /**
  1115. * Funtion calls the predefined function for provided control path
  1116. * @param {Object} value
  1117. * @param {Object} controlPath
  1118. * @param {Object} jsonObject
  1119. * @param {Object} node
  1120. *
  1121. * @private
  1122. */
  1123. var getFunctionValueForUndef = function (value, controlPath, jsonObject, node) {
  1124. if(typeof(value) === 'undefined'){
  1125. var functionPath = getFunctionPath(controlPath);
  1126. value = (functionPath && this.predefinedFunctions[functionPath])? this.predefinedFunctions[functionPath].call(node, jsonObject) : value;
  1127. }
  1128. return value;
  1129. };
  1130. /**
  1131. * Function returns data using attribute value and json object
  1132. *
  1133. * @param {Object} node
  1134. * @param {Object} path
  1135. * @param {Object} jsonObject
  1136. */
  1137. var getAttributeData = function (node, value, jsonObject, decode) {
  1138. if(decode){
  1139. value = decodeURI(value);
  1140. }
  1141. value = _stripBrackets(value);
  1142. if (false === value) {
  1143. return;
  1144. }
  1145. var data = _selector.resolveCondition.call(jsonObject, value);
  1146. data = getFunctionValueForUndef.call(this, data, value, jsonObject, node);
  1147. return data;
  1148. };
  1149. /**
  1150. * Function wraps getAttributeData function and allows for multiple json paths,
  1151. * in some attributes like fill, url etc
  1152. * @param {Object} node
  1153. * @param {Object} value
  1154. * @param {Object} jsonObject
  1155. * @param {Object} decode
  1156. */
  1157. var parseAttrubute = function(node, value, jsonObject, decode){
  1158. var self = this,
  1159. result = value.replace(/(\{[^\{\}]+\})/g, function($0, closuredJsonPath){
  1160. var data = getAttributeData.call(self, node, closuredJsonPath, jsonObject, decode);
  1161. return (data)? data : '';
  1162. });
  1163. return result;
  1164. };
  1165. /**
  1166. * Handler for fill attribute
  1167. * When renderObject() finds fill attribute in node, this function is called
  1168. * Registered using registerAttr()
  1169. *
  1170. * @param {Object} node
  1171. * @param {String} value of fill attribute
  1172. * @param {Object} jsonObject
  1173. */
  1174. var onAttrFill = function(node, value, jsonObject){
  1175. var data = parseAttrubute.call(this, node, value, jsonObject);
  1176. if (data) {
  1177. if (data instanceof Array) {
  1178. data = data.join(', ');
  1179. }
  1180. node.innerHTML = data;
  1181. return data;
  1182. }else{
  1183. _dom.hide(node);
  1184. return false;
  1185. }
  1186. };
  1187. /**
  1188. * Helper handler for onAttrHref and onAttrSrc
  1189. * This function is called when renderObject() finds the attribute <code>src</code>
  1190. * in a node.
  1191. * Registered using registerAttr()
  1192. *
  1193. * @param {Object} node
  1194. * @param {String} value of src attribute
  1195. * @param {Object} jsonObject
  1196. */
  1197. var onAttrUrl = function(node, value, jsonObject, attr){
  1198. var data = parseAttrubute.call(this, node, value, jsonObject, true),
  1199. nodeName = node.nodeName.toLowerCase();
  1200. if (typeof(data) === 'string') {
  1201. if(nodeName === 'a' || nodeName === 'link'){
  1202. node.href = data;
  1203. }else{
  1204. node.src = data;
  1205. }
  1206. }
  1207. return data;
  1208. };
  1209. /**
  1210. * Handler for attribute mailto inserts clicable link on node with email
  1211. *
  1212. * @param {Object} node
  1213. * @param {Object} value
  1214. * @param {Object} jsonObject
  1215. */
  1216. var onAttrMailto = function(node, value, jsonObject){
  1217. var data = parseAttrubute.call(this, node, value, jsonObject, true);
  1218. if (data) {
  1219. node.setAttribute('href', 'mailto:'+data);
  1220. }
  1221. return data;
  1222. };
  1223. /**
  1224. * Handler for attribute if hides or shows a node, depending on whether
  1225. * the specified condition is true or false.
  1226. *
  1227. * @param {Object} node
  1228. * @param {Object} value
  1229. * @param {Object} jsonObject
  1230. */
  1231. var onAttrIf = function (node, value, jsonObject) {
  1232. var data = getAttributeData.call(this, node, value, jsonObject);
  1233. if(!data){
  1234. _dom.hide(node);
  1235. return false;
  1236. }
  1237. };
  1238. /**
  1239. * Handler function for each attribute repeats inner html respectively for every element of the array
  1240. *
  1241. * @param {Object} node
  1242. * @param {Object} value
  1243. * @param {Object} jsonObject
  1244. */
  1245. var onAttrEach = function (node, value, jsonObject){
  1246. var data = getAttributeData.call(this, node, value, jsonObject);
  1247. if (data instanceof Array && data.length !== 0) {
  1248. var tempFrag = _dom.createFragment();
  1249. _dom.moveChildNodes(tempFrag, node);
  1250. for(var i=0, l=data.length; i<l; i++){
  1251. data[i]._index = (i+1);
  1252. fillNode.call(this, node, data[i], tempFrag);
  1253. }
  1254. }else{
  1255. _dom.hide(node);
  1256. }
  1257. return false;
  1258. };
  1259. /**
  1260. * Handler for tpl attribute. The attribute's value is the name of a template. This can be the
  1261. * id of the HTML that defines the template or the name of a standard template file provided with the
  1262. * API, for example, "nokia.general.place", "nokia.general.address", "nokia.general.contact", etc., where
  1263. * the last element is the base name of an HTML file.
  1264. * When renderObject() finds tpl attribute in node, this function is called
  1265. * Registered using registerAttr()
  1266. *
  1267. * @param {Object} node
  1268. * @param {String} value of tpl attribute
  1269. */
  1270. var onAttrTpl = function(node, value){
  1271. var el = _dom.byId(value);
  1272. if(el){
  1273. _dom.moveChildNodes(node, el.cloneNode(true));
  1274. node.removeAttribute('tpl');
  1275. }
  1276. };
  1277. /**
  1278. * Handler for rel attribute. The role of the attribute is primarily to assign events to
  1279. * HTML elements in a template, but also to identify HTML elements as targets for modules.
  1280. * @param {Object} node
  1281. * @param {Object} value
  1282. * @param {Object} jsonObject
  1283. */
  1284. var onAttrRel = function (node, value, jsonObject) {
  1285. var events = this.eventMap[value],
  1286. handler = function(event, node, jsonObject){
  1287. return function(e){
  1288. event.handler.call(node, jsonObject, e);
  1289. };
  1290. };
  1291. if (events) {
  1292. for (var i = 0, l = events.length; i < l; i++) {
  1293. var event = events[i];
  1294. if (event && event.name && typeof(event.handler) === 'function') {
  1295. _dom.addEvent(node, event.name, handler(event, node, jsonObject));
  1296. }
  1297. }
  1298. }
  1299. };
  1300. /**
  1301. * Function initializes the jsMotif object (creates template pattern)
  1302. * @private
  1303. */
  1304. var initialize = function(){
  1305. var tempElement = _dom.create('div',{});
  1306. tempElement.innerHTML = this.templateBody;
  1307. this.fragment = _dom.createFragment();
  1308. _dom.moveChildNodes(this.fragment, tempElement);
  1309. };
  1310. var jsMotifClass = function (params) {
  1311. this.targetElem = _dom.elem(params.targetNode) || _dom.elem(params.template);
  1312. this.mapping = [];
  1313. var templateHolder = _dom.elem(params.template);
  1314. if(templateHolder){
  1315. this.templateBody = templateHolder.innerHTML;
  1316. }else{
  1317. this.templateBody = params.template;
  1318. }
  1319. this.debug = params.debug || 0;
  1320. this.predefinedFunctions = params.functions || {};
  1321. this.attributes = [];
  1322. this.registerAttr('tpl', onAttrTpl);
  1323. this.registerAttr('if', onAttrIf);
  1324. this.registerAttr('each', onAttrEach);
  1325. this.registerAttr('fill', onAttrFill);
  1326. this.registerAttr('url', onAttrUrl);
  1327. this.registerAttr('rel', onAttrRel);
  1328. this.registerAttr('mailto', onAttrMailto);
  1329. this.eventMap = {};
  1330. this.addEventMap(params.events);
  1331. initialize.call(this);
  1332. };
  1333. /**
  1334. * Function collects the rel attributes and stores to 'this' context
  1335. * since it works recursively for child nodes use it within result object context
  1336. * @example
  1337. * var relMap = {};
  1338. * getRel.call(relMap, node);
  1339. *
  1340. * @param {Object} node
  1341. */
  1342. var getRel = function (node) {
  1343. var childNodes = node.childNodes,
  1344. child,
  1345. rel;
  1346. for(var i=0, l=childNodes.length; i<l; i++){
  1347. child = childNodes[i];
  1348. if(child.nodeName !== '#text' && child.nodeName !== '#comment'){
  1349. rel = child.getAttribute('rel');
  1350. if(rel){
  1351. this[rel] = child;
  1352. }
  1353. if (child.hasChildNodes()) {
  1354. getRel.call(this, child);
  1355. }
  1356. }
  1357. }
  1358. };
  1359. jsMotifClass.prototype = {
  1360. /**
  1361. * This method renders the HTML template, using the data and the target element
  1362. * specified by the caller.
  1363. * @param {Object} jsonObject An object containing the data to be rendered
  1364. * @param {Object} targetElem An optional target element in which to render the data; it
  1365. * is specified as {String) containing the DOM id or a node reference
  1366. * @param {Boolean} append An flag which tells if the template should append rendered element to target Node
  1367. * without clearing the node content
  1368. * @publish
  1369. */
  1370. render: function (jsonObject, targetElem /*?*/, append /*?*/) {
  1371. this.jsonObject = jsonObject;
  1372. var targetNode = (targetElem)? _dom.elem(targetElem) : this.targetElem;
  1373. if (targetNode) {
  1374. if(!append){
  1375. targetNode.innerHTML = '';
  1376. }
  1377. fillNode.call(this, targetNode , jsonObject, this.fragment);
  1378. this.targetElem = targetNode;
  1379. }else{
  1380. this.errorLog($E.LVL.critical, $E.MSG.noTargetNode, this);
  1381. }
  1382. },
  1383. /**
  1384. * This method renders the received data using the HTML template and returns the
  1385. * result as an HTML string. No events are attached.
  1386. * @param {Object} jsonObject An object containing the data to be rendered
  1387. * @reutrn An HTML string into which the received data have been rendered
  1388. * @publish
  1389. */
  1390. fetch: function (jsonObject) {
  1391. this.jsonObject = jsonObject;
  1392. var tempElement = _dom.create('div',{});
  1393. fillNode.call(this, tempElement, jsonObject, this.fragment);
  1394. return tempElement.innerHTML;
  1395. },
  1396. /**
  1397. * This method registers a handler for an attribute in a template DOM node.
  1398. * @param {String} name The name of the attribute as a string
  1399. * @param {Function} handler A caller-defined attribute handler function
  1400. * @publish
  1401. */
  1402. registerAttr: function(name, handler){
  1403. this.attributes.push({name: name, handler: handler});
  1404. },
  1405. /**
  1406. * This method adds an event handler to a template element.
  1407. * @param {Object} rel The value of the <code>rel</code> attribute in the template node
  1408. * @param {Object} name The name of the event, for example 'click', 'mouseout', etc.
  1409. * @param {Object} handler An event handler function defined by the caller
  1410. * @publish
  1411. */
  1412. addEvent: function(rel, name, handler) {
  1413. if(rel && name && handler){
  1414. if(!this.eventMap[rel]){
  1415. this.eventMap[rel] = [];
  1416. }
  1417. this.eventMap[rel].push({
  1418. name: name,
  1419. handler: handler
  1420. });
  1421. }
  1422. },
  1423. /**
  1424. * This method adds an event map to the template.
  1425. * @param {Object} events An array object assigning events and event handlers to a template node
  1426. * (see also the parameter <code>event</code> in the class contructor)
  1427. * @publish
  1428. */
  1429. addEventMap: function(events){
  1430. if(events && (events instanceof Array)){
  1431. for(var i=0, l=events.length; i<l; i++){
  1432. var event = events[i];
  1433. this.addEvent(event.rel, event.name, event.handler);
  1434. }
  1435. }
  1436. },
  1437. /**
  1438. * This method obtains a map object relating the <code>rel</code> attributes and the
  1439. * DOM elements to which they apply within the caller-supplied DOM node.
  1440. *
  1441. * @param {Object} node The DOM element in the template; all children of this element
  1442. * are checked for the <code>rel</code> attribute
  1443. * @return An {Object} mapping the <code>rel</code> attributes and DOM elements
  1444. * @publish
  1445. */
  1446. getRelMap: function (node) {
  1447. var relMap = {};
  1448. getRel.call(relMap, node);
  1449. return relMap;
  1450. },
  1451. /**
  1452. * Renders single node
  1453. * @param {Object} node
  1454. * @param {Object} jsonObject - data needed to render node
  1455. */
  1456. renderObject: function (node, jsonObject) {
  1457. var childNodes = node.childNodes,
  1458. i=0,
  1459. l=0,
  1460. value,
  1461. attribute,
  1462. attributeName,
  1463. processChildren;
  1464. if (node.getAttribute) {
  1465. for (i = 0, l = this.attributes.length; i < l; i++) {
  1466. attribute = this.attributes[i];
  1467. attributeName = attribute.name;
  1468. value = node.getAttribute(attributeName);
  1469. if(value){
  1470. /**
  1471. * If handler will return false, we shouldn't process children,
  1472. * one false should stop all inner processing!
  1473. */
  1474. var handlerResult = attribute.handler.call(this, node, value, jsonObject);
  1475. if(processChildren !== false){
  1476. processChildren = handlerResult;
  1477. }
  1478. if (attributeName !== 'rel' && attributeName !== 'tpl' && attributeName !== 'src' && attributeName !== 'href') {
  1479. node.removeAttribute(attributeName);
  1480. }
  1481. }
  1482. }
  1483. }
  1484. if(false === processChildren){
  1485. return false;
  1486. }
  1487. for(i=0, l=childNodes.length; i<l; i++){
  1488. var child = childNodes[i];
  1489. if (child.nodeName !== '#text' && child.nodeName !== '#comment') {
  1490. this.renderObject(child, jsonObject);
  1491. }
  1492. }
  1493. },
  1494. /**
  1495. * Function logs any errors related to wrong usage
  1496. * @param {Object} level
  1497. * @param {Object} params
  1498. */
  1499. errorLog: function(level, params/*, ...*/){
  1500. var args = Array.prototype.slice.call(arguments, 1);
  1501. if(this.debug >= level && 'object' === typeof(window.console)){
  1502. if(level === $E.LVL.log){
  1503. return window.console.log.apply(window.console, args);
  1504. }
  1505. if(level === $E.LVL.warn){
  1506. return window.console.warn.apply(window.console, args);
  1507. }
  1508. if(level === $E.LVL.critical){
  1509. return window.console.error.apply(window.console, args);
  1510. }
  1511. }
  1512. }
  1513. };
  1514. return jsMotifClass;
  1515. }());
  1516. jsMotif.Template.prototype.renderJSON = function(url, params, targetElem){
  1517. var self = this,
  1518. $comm = jsMotif.comm,
  1519. jsonFetcher = (params && params.reqType === 'jsonp')? $comm.jsonp : $comm.xhttp;
  1520. if(typeof(url) === 'string'){
  1521. jsonFetcher.getJson(url, function(data, status){
  1522. if(params && params.onData){
  1523. params.onData(data, status);
  1524. }
  1525. self.render(data, targetElem);
  1526. }, params);
  1527. }else{
  1528. self.render(url, targetElem);
  1529. }
  1530. };