Source: src/h5.ui.jqm.manager.js

  1. /*
  2. * Copyright (C) 2012-2016 NS Solutions Corporation
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. *
  16. * hifive
  17. */
  18. /* ------ h5.ui.jqm.manager ------ */
  19. (function() {
  20. // =========================================================================
  21. //
  22. // Constants
  23. //
  24. // =========================================================================
  25. var EV_NAME_H5_JQM_PAGE_HIDE = 'h5jqmpagehide';
  26. var EV_NAME_H5_JQM_PAGE_SHOW = 'h5jqmpageshow';
  27. var EV_NAME_EMULATE_PAGE_SHOW = 'h5controllerready.emulatepageshow';
  28. // =============================
  29. // Production
  30. // =============================
  31. // エラーコード
  32. var ERR_CODE_INVALID_TYPE = 12000;
  33. var ERR_CODE_NAME_INVALID_PARAMETER = 12001;
  34. // =============================
  35. // Development Only
  36. // =============================
  37. var fwLogger = h5.log.createLogger('h5.ui.jqm.manager');
  38. /* del begin */
  39. var FW_LOG_JQM_CONTROLLER_ALREADY_INITIALIZED = '既にJQMマネージャは初期化されています。';
  40. var FW_LOG_CONTROLLER_DEF_ALREADY_DEFINED = '既にコントローラ"{0}"はJQMマネージャに登録されています。';
  41. var FW_LOG_CSS_FILE_PATH_ALREADY_DEFINED = '既にCSSファイル"{0}"はJQMマネージャに登録されています。';
  42. // エラーコードマップ
  43. var errMsgMap = {};
  44. errMsgMap[ERR_CODE_INVALID_TYPE] = '引数{0}が不正です。正しい値を指定して下さい。';
  45. errMsgMap[ERR_CODE_NAME_INVALID_PARAMETER] = '引数の指定に誤りがあります。第2引数にCSSファイルパス、第3引数にコントローラ定義オブジェクトを指定して下さい。';
  46. addFwErrorCodeMap(errMsgMap);
  47. /* del end */
  48. // =========================================================================
  49. //
  50. // Cache
  51. //
  52. // =========================================================================
  53. // =========================================================================
  54. //
  55. // Privates
  56. //
  57. // =========================================================================
  58. // =============================
  59. // Variables
  60. // =============================
  61. /**
  62. * ページをの初期化処理呼び出しをpagecreateイベントハンドラで行うかどうか
  63. * <p>
  64. * このフラグがtrueの場合はpagecreateイベントハンドラでページの初期化処理を呼び出します。
  65. * falseの場合はpageinitイベントハンドラでページの初期化処理を呼び出します。
  66. * </p>
  67. * <p>
  68. * jqm1.4より前ではpagecreateイベントはページのDOM拡張前、pageinitイベントはページのDOM拡張後に上がりますが、
  69. * jqm1.4以降ではDOM拡張後にpagecreateが上がり、pageinitイベントはdeprecatedになりました。
  70. * jqm1.4以降ではdeprecatedなpageinitではなくpagecreateイベントハンドラで初期化処理を行うようにします。
  71. * </p>
  72. * <p>
  73. * フラグの値はh5.ui.jqm.manager.init時に設定します。
  74. * </p>
  75. */
  76. var shouldHandlePagecreateEvent;
  77. /**
  78. * JQMControllerのインスタンス(シングルトン)
  79. */
  80. var jqmControllerInstance = null;
  81. /**
  82. * コントローラのマップ
  83. * <p>
  84. * キー:ページID、値:コントローラ定義オブジェクト
  85. *
  86. * @type Object
  87. */
  88. var controllerMap = {};
  89. /**
  90. * コントローラインスタンスのマップ
  91. * <p>
  92. * キー:ページID、値:コントローラインスタンスの配列
  93. *
  94. * @type Object
  95. */
  96. var controllerInstanceMap = {};
  97. /**
  98. * JQMManagerで管理する、h5.core.controller()で動的に生成されたコントローラインスタンスを保持するマップ
  99. * <p>
  100. * キー:ページID、値:コントローラインスタンスの配列
  101. *
  102. * @type Object
  103. */
  104. var dynamicControllerInstanceMap = {};
  105. /**
  106. * 初期化パラメータのマップ
  107. * <p>
  108. * キー:ページID、値:初期化パラメータの配列
  109. *
  110. * @type Object
  111. */
  112. var initParamMap = {};
  113. /**
  114. * CSSファイルのマップ
  115. * <p>
  116. * キー:ページID、値:CSSファイルパスのオブジェクトの配列
  117. *
  118. * @type Object
  119. */
  120. var cssMap = {};
  121. /**
  122. * h5.ui.jqm.manager.init()が呼ばれたかどうかを示すフラグ
  123. *
  124. * @type Boolean
  125. */
  126. var isInitCalled = false;
  127. /**
  128. * pagehideイベントが発生したかを判定するフラグ
  129. *
  130. * @type Boolean
  131. */
  132. var hideEventFired = false;
  133. /**
  134. * 初期表示時、アクティブページにバインドしたコントローラがreadyになるよりも前に、 pageshowが発火したかを判定するフラグ
  135. *
  136. * @type Boolean
  137. */
  138. var showEventFiredBeforeReady = false;
  139. // =============================
  140. // Functions
  141. // =============================
  142. /**
  143. * アクティブページに設定されているID属性の値を取得します。
  144. * <p>
  145. * アクティブページが存在しない、またはIDが1文字以上の文字列ではない場合nullを取得します。
  146. */
  147. function getActivePageId() {
  148. var $ap = $.mobile.activePage;
  149. var id = $ap && $ap[0] && $ap[0].id;
  150. return isString(id) && id.length > 0 ? id : null;
  151. }
  152. /**
  153. * 現在のアクティブページにコントローラをバインドします。
  154. */
  155. function bindToActivePage() {
  156. var id = getActivePageId();
  157. if (id === null) {
  158. return;
  159. }
  160. // jqmControllerInstanceにインスタンスが格納されるのはinitの中で$(function(){})で囲って行っているため、
  161. // bindToActivePageがdocument.readyより前に呼ばれた場合はjqmControllerInstanceに値がまだ入っていない場合がある。
  162. // そのためjqmControllerInstanceのメソッド呼び出しは$(function(){})で囲って行っている。
  163. $(function() {
  164. jqmControllerInstance.addCSS(id);
  165. jqmControllerInstance.bindController(id);
  166. });
  167. }
  168. /**
  169. * コントローラインスタンスの__nameプロパティとコントローラ定義オブジェクトの__nameプロパティを比較し、同値であるかを判定します。
  170. *
  171. * @param {Object[]} controllerInstances コントローラインスタンスを保持する配列
  172. * @param {Object} controllerDefObj コントローラ定義オブジェクト
  173. */
  174. function equalsControllerName(controllerInstances, controllerDefObj) {
  175. var ret = false;
  176. for (var i = 0, len = controllerInstances.length; i < len; i++) {
  177. var ci = controllerInstances[i];
  178. if (ci && ci.__name === controllerDefObj.__name) {
  179. ret = true;
  180. break;
  181. }
  182. }
  183. return ret;
  184. }
  185. /**
  186. * JQMマネージャが管理する動的または静的コントローラが持つイベントハンドラの有効・無効化を行います
  187. *
  188. * @param {String} id ページID
  189. * @param {Boolean} flag (true: ハンドラを有効化する / false: ハンドラを無効化する)
  190. */
  191. function changeListenerState(id, flag) {
  192. for (var prop in controllerInstanceMap) {
  193. var controllers = controllerInstanceMap[prop];
  194. var pageControllerEnabled = id === prop;
  195. for (var i = 0, len = controllers.length; i < len; i++) {
  196. var controller = controllers[i];
  197. if (pageControllerEnabled) {
  198. controller[(flag ? 'enable' : 'disable') + 'Listeners']();
  199. }
  200. }
  201. }
  202. for (var prop in dynamicControllerInstanceMap) {
  203. var dynamicControllers = dynamicControllerInstanceMap[prop];
  204. var dynamicControllerEnabled = id === prop;
  205. for (var i = 0, len = dynamicControllers.length; i < len; i++) {
  206. var dynamicController = dynamicControllers[i];
  207. if (dynamicControllerEnabled) {
  208. dynamicController[(flag ? 'enable' : 'disable') + 'Listeners']();
  209. }
  210. }
  211. }
  212. }
  213. /**
  214. * バージョン文字列の大小を比較する関数
  215. * <p>
  216. * '1.11.0', '1.9.9'のような'.'区切りのバージョン文字列を比較して、第1引数の方が小さければ-1、同じなら0、第2引数の方が小さければ1 を返す。
  217. * </p>
  218. *
  219. * @param {String} a バージョン文字列
  220. * @param {String} b バージョン文字列
  221. * @returns {Integer} 比較結果。aがbより小さいなら-1、同じなら0、aがbより大きいなら1 を返す
  222. */
  223. function compareVersion(a, b) {
  224. // '.0'が末尾にならない様にする
  225. a = a.replace(/(\.0+)+$/, '');
  226. b = b.replace(/(\.0+)+$/, '');
  227. if (a === b) {
  228. // aとbが同じならループで比較せずに0を返す
  229. return 0;
  230. }
  231. var aAry = a.split('.');
  232. var bAry = b.split('.');
  233. var aAryLen = aAry.length;
  234. for (var i = 0; i < aAryLen; i++) {
  235. if (bAry[i] == null) {
  236. // bAryが先にnullになった=aAryの方が桁数(バージョン文字列の.の数)が多い場合、
  237. // '.0'が末尾にならないようにしてあるので、桁数の多い方がバージョンが大きい
  238. return 1;
  239. }
  240. var aVal = parseInt(aAry[i], 10);
  241. var bVal = parseInt(bAry[i], 10);
  242. if (aVal === bVal) {
  243. // 同じなら次以降のindexで比較
  244. continue;
  245. }
  246. // 比較してaが小さいなら-1、bが小さいなら-1を返す
  247. return aVal < bVal ? -1 : 1;
  248. }
  249. if (bAry[aAryLen] != null) {
  250. // aAryよりbAryの方が桁数が多い場合はbの方が桁数が多いのでバージョンが大きい
  251. return -1;
  252. }
  253. // 最後まで比較して同じなら同じバージョンなので0を返す
  254. return 0;
  255. }
  256. // =========================================================================
  257. //
  258. // Body
  259. //
  260. // =========================================================================
  261. /**
  262. * @name jqm
  263. * @memberOf h5.ui
  264. * @namespace
  265. */
  266. h5.u.obj.ns('h5.ui.jqm');
  267. /**
  268. * hifiveで使用するdata属性のプレフィックス。<br />
  269. * デフォルトは"h5"。
  270. *
  271. * @type String
  272. * @memberOf h5.ui.jqm
  273. * @name dataPrefix
  274. */
  275. h5.ui.jqm.dataPrefix = 'h5';
  276. /**
  277. * JQMコントローラ
  278. */
  279. var jqmController = {
  280. /**
  281. * コントローラ名
  282. *
  283. * @memberOf JQMController
  284. */
  285. __name: 'JQMController',
  286. /**
  287. * __readyイベントのハンドラ
  288. *
  289. * @param {Object} context コンテキスト
  290. * @memberOf JQMController
  291. */
  292. __ready: function() {
  293. var that = this;
  294. var activePageId = getActivePageId();
  295. $(':jqmData(role="page"), :jqmData(role="dialog")').each(function() {
  296. that.loadScript(this.id);
  297. });
  298. var $page = this.$find('#' + activePageId);
  299. // 初期表示時、トランジションにアニメーションが適用されていない場合、
  300. // JQMコントローラがreadyになる前にpageshowが発火してしまいJQMコントローラが拾うことができないため、
  301. // 既にpageshowが発火されていたら、h5controllerreadyのタイミングで、h5jqmpageshowをトリガする
  302. $page.one(EV_NAME_EMULATE_PAGE_SHOW, function() {
  303. if (showEventFiredBeforeReady && $page[0] === $.mobile.activePage[0]) {
  304. $page.trigger(EV_NAME_H5_JQM_PAGE_SHOW, {
  305. prevPage: $('')
  306. });
  307. }
  308. });
  309. },
  310. /**
  311. * pageinitイベントのハンドラ
  312. *
  313. * @param {Object} context コンテキスト
  314. * @memberOf JQMController
  315. */
  316. ':jqmData(role="page"), :jqmData(role="dialog") pageinit': function(context) {
  317. // pagecreateイベントを使うべきである場合はpagecreateハンドラ、そうでない時はpageinitハンドラで初期化処理を行う。
  318. if (!shouldHandlePagecreateEvent) {
  319. this._initPage(context.event.target.id);
  320. }
  321. },
  322. /**
  323. * pagecreateイベントのハンドラ
  324. *
  325. * @param {Object} context コンテキスト
  326. * @memberOf JQMController
  327. */
  328. ':jqmData(role="page"), :jqmData(role="dialog") pagecreate': function(context) {
  329. if (shouldHandlePagecreateEvent) {
  330. this._initPage(context.event.target.id);
  331. }
  332. },
  333. /**
  334. * ページの初期化処理を行う
  335. *
  336. * @private
  337. * @param {String} id
  338. * @memberOf JQMController
  339. */
  340. _initPage: function(id) {
  341. this.loadScript(id);
  342. this.addCSS(id);
  343. this.bindController(id);
  344. },
  345. /**
  346. * pageremoveイベントのハンドラ
  347. *
  348. * @param {Object} context コンテキスト
  349. * @memberOf JQMController
  350. */
  351. '{rootElement} pageremove': function(context) {
  352. // pagehide -> pageremoveの順でイベントは発火するので、pagehideのタイミングでh5jqmpagehideをトリガすればよいが、
  353. // 別ページに遷移する際、JQMがpagebeforeloadからpageloadの間のタイミングで、pageremoveをトリガするハンドラをpagehide.removeにバインドしてしまう為、
  354. // これ以降にpagehideに対して登録したハンドラは全てpageremoveの後に発火してしまう
  355. // 上記の理由により、pageremoveが発生する場合は、このタイミングでh5jqmpagehideイベントをトリガし、
  356. // pagehideイベントではh5jqmpagehideイベントを発火しないようフラグで制御する
  357. $(context.event.target).trigger(EV_NAME_H5_JQM_PAGE_HIDE, {
  358. nextPage: $.mobile.activePage
  359. });
  360. hideEventFired = true;
  361. var id = context.event.target.id;
  362. var controllers = controllerInstanceMap[id];
  363. var dynamicControllers = dynamicControllerInstanceMap[id];
  364. if (controllers) {
  365. for (var i = 0, len = controllers.length; i < len; i++) {
  366. controllers[i].dispose();
  367. }
  368. controllerInstanceMap[id] = [];
  369. }
  370. if (dynamicControllers) {
  371. for (var i = 0, len = dynamicControllers.length; i < len; i++) {
  372. dynamicControllers[i].dispose();
  373. }
  374. dynamicControllerInstanceMap[id] = [];
  375. }
  376. },
  377. /**
  378. * pagebeforeshowイベントのハンドラ
  379. *
  380. * @param {Object} context コンテキスト
  381. * @memberOf JQMController
  382. */
  383. '{rootElement} pagebeforeshow': function(context) {
  384. var id = context.event.target.id;
  385. this.addCSS(id);
  386. changeListenerState(id, true);
  387. },
  388. /**
  389. * pagehideイベントのハンドラ コントローラでもページ非表示時のイベントを拾えるようにするため、
  390. * JQMのpagehideイベントと同じタイミングで、JQMマネージャが管理しているコントローラのルート要素に対してh5jqmpagehideイベントをトリガします
  391. * h5jqmpagehideイベントをトリガ後アクティブページ以外のページに対して、コントローラのイベントハンドラの無効化と、 CSSの定義をHEADタグから削除を行います
  392. *
  393. * @param {Object} context コンテキスト
  394. * @memberOf JQMController
  395. */
  396. '{rootElement} pagehide': function(context) {
  397. if (!hideEventFired) {
  398. $(context.event.target).trigger(EV_NAME_H5_JQM_PAGE_HIDE, {
  399. nextPage: context.evArg.nextPage
  400. });
  401. }
  402. hideEventFired = false;
  403. var id = context.event.target.id;
  404. changeListenerState(id, false);
  405. this.removeCSS(id);
  406. },
  407. /**
  408. * コントローラでもページ表示時のイベントを拾えるようにするため、 JQMのpageshowイベントと同じタイミングで、JQMマネージャが管理しているコントローラのルート要素に対して
  409. * h5jqmpageshowイベントをトリガします
  410. *
  411. * @param {Object} context コンテキスト
  412. * @memberOf JQMController
  413. */
  414. '{rootElement} pageshow': function(context) {
  415. var emulatePageShow = false;
  416. var $target = $(context.event.target);
  417. var $fromPage = context.evArg ? context.evArg.prevPage : $('');
  418. var conAr = controllerInstanceMap[$target[0].id];
  419. if (conAr) {
  420. for (var i = 0, len = conAr.length; i < len; i++) {
  421. var controllerInstance = conAr[i];
  422. // isReady=falseであるときコントローラのイベントハンドラは無効であり、
  423. // JQMマネージャが管理する静的コントローラがイベントを受け取れない状態なので、h5controllerready後にh5jqmpageshowをトリガするようにする
  424. // トランジションのアニメーションが無効(同期でJQMのイベントが発生する)場合のみここに到達する
  425. if (!controllerInstance.isReady) {
  426. $target.unbind(EV_NAME_EMULATE_PAGE_SHOW).one(EV_NAME_EMULATE_PAGE_SHOW,
  427. function() {
  428. if ($.mobile.activePage[0] === $target[0]) {
  429. $target.trigger(EV_NAME_H5_JQM_PAGE_SHOW, {
  430. prevPage: $fromPage
  431. });
  432. }
  433. });
  434. emulatePageShow = true;
  435. break;
  436. }
  437. }
  438. }
  439. if (!emulatePageShow) {
  440. $target.trigger(EV_NAME_H5_JQM_PAGE_SHOW, {
  441. prevPage: $fromPage
  442. });
  443. }
  444. },
  445. /**
  446. * h5controllerboundイベントを監視しコントローラインスタンスを管理するためのイベントハンドラ
  447. *
  448. * @param {Object} context コンテキスト
  449. * @memberOf JQMController
  450. */
  451. '{rootElement} h5controllerbound': function(context) {
  452. var boundController = context.evArg;
  453. if (this === boundController) {
  454. return;
  455. }
  456. var id = getActivePageId();
  457. if (id === null) {
  458. return;
  459. }
  460. // define()でバインドしたコントローラも、h5controllerboundイベントを発火するので、
  461. // このイベントを発生させたコントローラが、define()によってバインドしたコントローラか判定する
  462. // ↑がtrue = 「既にJQMManagerの管理対象になっているコントローラ」なので、dynamicControllerInstanceMapに含めない
  463. if ($.inArray(boundController, controllerInstanceMap[id]) !== -1) {
  464. return;
  465. }
  466. if (!dynamicControllerInstanceMap[id]) {
  467. dynamicControllerInstanceMap[id] = [];
  468. }
  469. dynamicControllerInstanceMap[id].push(boundController);
  470. },
  471. /**
  472. * 動的に生成されたコントローラがunbindまたはdisposeされた場合、JQMManagerの管理対象から除外します
  473. *
  474. * @param context コンテキスト
  475. * @memberOf JQMController
  476. */
  477. '{rootElement} h5controllerunbound': function(context) {
  478. var unboundController = context.evArg;
  479. var id = getActivePageId();
  480. if (id === null) {
  481. return;
  482. }
  483. var index = $.inArray(unboundController, dynamicControllerInstanceMap[id]);
  484. if (index === -1) {
  485. return;
  486. }
  487. dynamicControllerInstanceMap[id].splice(index, 1);
  488. },
  489. /**
  490. * 指定されたページIDに紐付くスクリプトをロードする。
  491. *
  492. * @param {String} id ページID
  493. * @memberOf JQMController
  494. */
  495. loadScript: function(id) {
  496. var page = $('#' + id);
  497. var script = $.trim(page.data(this.getDataAttribute('script')));
  498. if (script.length === 0) {
  499. return;
  500. }
  501. var src = $.map(script.split(','), function(n) {
  502. return $.trim(n);
  503. });
  504. var async = page.data(this.getDataAttribute('async')) == true;
  505. return h5.u.loadScript(src, {
  506. async: async
  507. });
  508. },
  509. /**
  510. * JQMコントローラが使用するdata属性にprefixを付けた属性名を返す。
  511. *
  512. * @param {String} attributeName 属性名
  513. * @returns {String} prefixを付けた属性名
  514. */
  515. getDataAttribute: function(attributeName) {
  516. var prefix = h5.ui.jqm.dataPrefix;
  517. if (prefix == null) {
  518. prefix = 'h5';
  519. }
  520. return prefix.length !== 0 ? prefix + '-' + attributeName : attributeName;
  521. },
  522. /**
  523. * コントローラのバインドを行う
  524. *
  525. * @param {String} id ページID
  526. * @memberOf JQMController
  527. */
  528. bindController: function(id) {
  529. var controllerDefs = controllerMap[id];
  530. var initParams = initParamMap[id];
  531. if (!controllerDefs || controllerDefs.length === 0) {
  532. return;
  533. }
  534. if (!controllerInstanceMap[id]) {
  535. controllerInstanceMap[id] = [];
  536. }
  537. var ci = controllerInstanceMap[id];
  538. for (var i = 0, len = controllerDefs.length; i < len; i++) {
  539. var defObj = controllerDefs[i];
  540. if (equalsControllerName(ci, defObj)) {
  541. continue;
  542. }
  543. controllerInstanceMap[id].push(h5.core.controller('#' + id, defObj, initParams[i]));
  544. }
  545. },
  546. /**
  547. * 指定されたページIDに紐付くCSSをHEADに追加する。
  548. *
  549. * @param {String} id ページID
  550. * @memberOf JQMController
  551. */
  552. addCSS: function(id) {
  553. var src = cssMap[id];
  554. if (!src) {
  555. return;
  556. }
  557. var head = document.getElementsByTagName('head')[0];
  558. var linkTags = head.getElementsByTagName('link');
  559. var linkLen = linkTags.length;
  560. for (var i = 0, srcLen = src.length; i < srcLen; i++) {
  561. var path = $.mobile.path.parseUrl(src[i]).filename;
  562. var isLoaded = false;
  563. for (var j = 0; j < linkLen; j++) {
  564. var loadedPath = $.mobile.path.parseUrl(linkTags[j].href).filename;
  565. if (loadedPath === path) {
  566. isLoaded = true;
  567. break;
  568. }
  569. }
  570. if (isLoaded) {
  571. continue;
  572. }
  573. var cssNode = document.createElement('link');
  574. cssNode.type = 'text/css';
  575. cssNode.rel = 'stylesheet';
  576. cssNode.href = src[i];
  577. head.appendChild(cssNode);
  578. }
  579. },
  580. /**
  581. * 指定されたページIDに紐付くCSSを削除する。
  582. *
  583. * @param {String} id ページID
  584. * @memberOf JQMController
  585. */
  586. removeCSS: function(id) {
  587. var fromPageCSS = cssMap[id];
  588. if (!fromPageCSS) {
  589. return;
  590. }
  591. var id = getActivePageId();
  592. if (id === null) {
  593. return;
  594. }
  595. var toPageCSS = cssMap[id];
  596. $('link').filter(function() {
  597. var href = $(this).attr('href');
  598. // 遷移元のページで使用していたCSSファイルを、遷移先のページでも使用する場合はremoveしない
  599. return $.inArray(href, fromPageCSS) !== -1 && $.inArray(href, toPageCSS) === -1;
  600. }).remove();
  601. }
  602. };
  603. // =============================
  604. // Expose to window
  605. // =============================
  606. /**
  607. * jQuery Mobile専用コントローラマネージャ (JQM Manager)
  608. * <p>
  609. * 現在表示されているページ(アクティブページ)に指定したコントローラとCSSファイルを読み込むという定義を行うことにより、 jQuery
  610. * Mobile特有のページのライフサイクル(DOM生成や破棄)にあわせて、ページに対して
  611. * <ul>
  612. * <li>コントローラで定義したハンドラ</li>
  613. * <li>スタイル(CSSファイル)</li>
  614. * </ul>
  615. * の有効・無効化を行います。
  616. * <p>
  617. * JQM Managerはページ内で動的に作成した(h5.core.controller()で生成した)コントローラも管理します。<br>
  618. * 動的に生成したコントローラもページに紐づくものとして管理し、define()で生成したコントローラと同様に、ページの遷移によってハンドラとスタイルの有効・無効化を行います。
  619. * <p>
  620. * 動的に生成されたコントローラは、そのページに内で起こったユーザー操作などによって初めて作られるものと考えられるため、
  621. * pageremoveイベントが発生してページが破棄されると、そのページに紐づいたコントローラのインスタンスも破棄されます。
  622. *
  623. * @name manager
  624. * @memberOf h5.ui.jqm
  625. * @namespace
  626. */
  627. h5.u.obj.expose('h5.ui.jqm.manager',
  628. {
  629. /**
  630. * jQuery Mobile用hifiveコントローラマネージャを初期化します。
  631. * <p>
  632. * 2回目以降は何も処理を行いません。
  633. *
  634. * @memberOf h5.ui.jqm.manager
  635. * @function
  636. * @name init
  637. */
  638. init: function() {
  639. if (isInitCalled) {
  640. fwLogger.info(FW_LOG_JQM_CONTROLLER_ALREADY_INITIALIZED);
  641. return;
  642. }
  643. isInitCalled = true;
  644. // jqmのバージョンを見てpagecreateイベントのタイミングで初期化するべきかどうかのフラグの値をセットする
  645. // (initが呼ばれるタイミングではjqmが読み込まれている前提)
  646. shouldHandlePagecreateEvent = compareVersion($.mobile.version, '1.4') >= 0;
  647. // 初期表示時、JQMマネージャがreadyになる前にpageshowイベントが発火したかをチェックする
  648. $(document).one('pageshow', function() {
  649. showEventFiredBeforeReady = true;
  650. });
  651. $(function() {
  652. jqmControllerInstance = h5internal.core.controllerInternal('body',
  653. jqmController, null, {
  654. managed: false
  655. });
  656. bindToActivePage();
  657. });
  658. },
  659. /**
  660. * jQuery Mobile用hifiveコントローラマネージャにコントローラを登録します。
  661. * <p>
  662. * 「data-role="page"」または「data-role="dialog"」の属性が指定された要素でかつ、
  663. * idが第1引数で指定されたものに一致する要素に対してコントローラを登録します。
  664. * <p>
  665. * 1つのページに複数コントローラを登録することもできます。<br>
  666. * 以下のように、登録したいコントローラ定義オブジェクトの数分、define()を実行して下さい。
  667. *
  668. * <pre>
  669. * h5.ui.jqm.manager.define('pageA', 'css/pageA.css', controllerDefA, defAParams);
  670. * h5.ui.jqm.manager.define('pageA', 'css/pageA.css', controllerDefB, defBParams);
  671. * </pre>
  672. *
  673. * 注意:<br>
  674. * ただし、ページに同じコントローラを2つ以上バインドすることはできません。<br>
  675. * 同じコントローラであるかの判定は、コントローラ定義オブジェクトの<b>__name</b>プロパティの値がバインド済みのコントローラと同値であるか比較し、同値の場合はバインドされません。
  676. *
  677. * @param {String} id ページID
  678. * @param {String|String[]} [cssSrc] CSSファイルのパス
  679. * @param {Object} [controllerDefObject] コントローラ定義オブジェクト
  680. * @param {Object} [initParam] 初期化パラメータ (ライフサイクルイベント(__construct, __init,
  681. * __ready)の引数にargsプロパティとして渡されます)
  682. * @memberOf h5.ui.jqm.manager
  683. * @function
  684. * @name define
  685. */
  686. define: function(id, cssSrc, controllerDefObject, initParam) {
  687. if (!isString(id)) {
  688. throwFwError(ERR_CODE_INVALID_TYPE, 'id');
  689. }
  690. if (cssSrc != null && !isString(cssSrc) && !isArray(cssSrc)) {
  691. throwFwError(ERR_CODE_INVALID_TYPE, 'cssSrc');
  692. }
  693. if (controllerDefObject != null) {
  694. if (isString(controllerDefObject) || isArray(controllerDefObject)) {
  695. throwFwError(ERR_CODE_NAME_INVALID_PARAMETER);
  696. }
  697. if (!$.isPlainObject(controllerDefObject)
  698. || !('__name' in controllerDefObject)) {
  699. throwFwError(ERR_CODE_INVALID_TYPE, 'controllerDefObject');
  700. }
  701. if (initParam != null && !$.isPlainObject(initParam)) {
  702. throwFwError(ERR_CODE_INVALID_TYPE, 'initParam');
  703. }
  704. }
  705. if (!cssMap[id]) {
  706. cssMap[id] = [];
  707. }
  708. if (!controllerMap[id]) {
  709. controllerMap[id] = [];
  710. }
  711. if (!initParamMap[id]) {
  712. initParamMap[id] = [];
  713. }
  714. $.merge(cssMap[id], $.map($.makeArray(cssSrc), function(val, i) {
  715. if ($.inArray(val, cssMap[id]) !== -1) {
  716. fwLogger.info(FW_LOG_CSS_FILE_PATH_ALREADY_DEFINED, val);
  717. return null;
  718. }
  719. return val;
  720. }));
  721. if (controllerDefObject) {
  722. if ($.inArray(controllerDefObject, controllerMap[id]) === -1) {
  723. controllerMap[id].push(controllerDefObject);
  724. initParamMap[id].push(initParam);
  725. } else {
  726. fwLogger.info(FW_LOG_CONTROLLER_DEF_ALREADY_DEFINED,
  727. controllerDefObject.__name);
  728. }
  729. }
  730. if (isInitCalled && getActivePageId() !== null) {
  731. bindToActivePage();
  732. } else {
  733. this.init();
  734. }
  735. }
  736. /* del begin */
  737. ,
  738. /*
  739. * テスト用に公開
  740. * JQMControllerが管理しているコントローラへの参照と、JQMControllerインスタンスへの参照を除去し、JQMControllerをdisposeをします。
  741. *
  742. * @memberOf h5.ui.jqm.manager
  743. * @function
  744. * @name __reset
  745. */
  746. __reset: function() {
  747. if (jqmControllerInstance) {
  748. jqmControllerInstance.dispose();
  749. jqmControllerInstance = null;
  750. }
  751. controllerMap = {};
  752. controllerInstanceMap = {};
  753. dynamicControllerInstanceMap = {};
  754. initParamMap = {};
  755. cssMap = {};
  756. isInitCalled = false;
  757. hideEventFired = false;
  758. showEventFiredBeforeReady = false;
  759. }
  760. /* del end */
  761. });
  762. })();