Geen omschrijving

spa.js 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  1. $.define(["jQuery", "util", "doc", "win", "body"], "spa", function($, util, doc, win, body) {
  2. var head = doc.head || doc.getElementsByTagName("head")[0] || doc.documentElement,
  3. baseElement = head.getElementsByTagName("base")[0],
  4. ahref = { an: "href", av: "javascript:;" },
  5. spa_modal_index = 0,
  6. spa_css_ref = 0,
  7. resCache = {},
  8. cssCache = {},
  9. htmlCache = { "#": "#" },
  10. modelCache = {"_def_error_form":{}},
  11. scriptCache = {}, resUri, menuUri, menuEle,
  12. main, mainEle,
  13. cfg = {
  14. ajaxCfg: { mask: true },
  15. mask: true,
  16. loadEnabled: true
  17. },
  18. load_res = function() {
  19. if(resUri) {
  20. util.get(resUri, null, function(data) {
  21. /**
  22. * res =[{id:"",uri:"",css:"",script:""},{id:"",uri:"",css:"",script:""},...........]
  23. */
  24. if(data && data.length) {
  25. data.forEach(function(item) {
  26. resCache[item.id] = item;
  27. });
  28. }
  29. menuUri ? load_menu() : showMain();
  30. },false, cfg.ajaxCfg);
  31. }
  32. },
  33. load_menu = function() {
  34. util.get(menuUri, null, function(menu) {
  35. build_menu(menu);
  36. showMain();
  37. },false, cfg.ajaxCfg);
  38. },
  39. build_menu = function(menu) {
  40. if(menuEle && menuEle.length && menu && menu.length) {
  41. var ul = { tn: "ul", attrs: [{ an: "class", av: "nav nav-root" }], chs: [] };
  42. build_menu_item(ul.chs, menu);
  43. menuEle.empty();
  44. util.appendChild(menuEle[0], ul);
  45. menuEle.find(".nav-hand").on("click", function(e) {
  46. var $this = $(this);
  47. menuEle.find(".nav-hand.active").removeClass("active");
  48. $this.addClass("active");
  49. if($this.hasClass("spa-modal")) {
  50. showModal($this.attr("res"));
  51. } else {
  52. location.hash = "#" + $this.attr("res");
  53. showMain();
  54. }
  55. });
  56. menuEle.find(".nav-branch-hand").on("click", function(e) {
  57. var prt = $(this).parent();
  58. if(prt.hasClass("open")) {
  59. prt.removeClass("open");
  60. } else {
  61. prt.parent().children(".open").removeClass("open");
  62. prt.addClass("open");
  63. }
  64. });
  65. }
  66. },
  67. build_menu_item = function(po, items) {
  68. var item, res, caption, iconClass;
  69. items.forEach(function(item) {
  70. var li = { tn: "li" },
  71. attrs = li.attrs = [],
  72. chses = li.chs = [],
  73. lia = { tn: "a", attrs: [ahref], chs: [] };
  74. chses.push(lia);
  75. var ic = "icon-" + (item.icon || (item.res ? "book" : "branch"));
  76. lia.chs.push({ tn: "i", attrs: [{ an: "class", av: ic }] });
  77. lia.chs.push(item.caption);
  78. var lac = "";
  79. if(item.res) {
  80. lia.attrs.push({ an: "res", av: item.res });
  81. lac = lac + (item.modal ? " nav-hand spa-modal" : " nav-hand");
  82. lia.attrs.push({ an: "class", av: lac })
  83. } else {
  84. lia.chs.push({ tn: "i", attrs: [{ an: "class", av: "icon fold" }] });
  85. lac = lac + " nav-branch-hand";
  86. attrs.push({ an: "class", av: "nav-parent" });
  87. var ul = { tn: "ul", attrs: [{ an: "class", av: "nav" }], chs: [] };
  88. chses.push(ul);
  89. build_menu_item(ul.chs, item.children);
  90. }
  91. lia.attrs.push({ an: "class", av: lac });
  92. po.push(li);
  93. });
  94. },
  95. loadModelCss = function(model) {
  96. if(model.css) {
  97. var uri = model.css;
  98. if(cssCache[uri]) {
  99. cssCache[uri]['ref'] = cssCache[uri]['ref'] + 1;
  100. } else {
  101. ++spa_css_ref;
  102. var css_id = "___spa_css_id_" + spa_css_ref;
  103. cssCache[uri] = { ref: 1, id: css_id };
  104. var link = doc.createElement('link');
  105. link.rel = 'stylesheet';
  106. link.href = model.css;
  107. link.media = 'all';
  108. link.setAttribute("id", css_id);
  109. head.appendChild(link);
  110. }
  111. }
  112. },
  113. showMainInternal = function(model) {
  114. cleanMain();
  115. main = model;
  116. loadModelCss(model);
  117. if(model.uri) {
  118. mainEle.html(htmlCache[model.uri]);
  119. }
  120. mainEle.attr("spa-model-id", model.id);
  121. if(model.factory && model.factory.main) {
  122. model.factory.main.call();
  123. }
  124. },
  125. showModalInternal = function(model, data) {
  126. loadModelCss(model);
  127. var ly = util.showModal(model.uri ? htmlCache[model.uri] : null);
  128. ++spa_modal_index;
  129. ly.ctn.addClass("spa-modal").addClass("spa-modal-index-" + spa_modal_index).attr("spa-model-id", model.id);
  130. if(model.factory && model.factory.modal) {
  131. model.factory.modal(data);
  132. }
  133. },
  134. cacheModel = function(model) {
  135. var m = modelCache[model.id] = {};
  136. m.uri = model.uri;
  137. m.factory = model.factory;
  138. m.css = model.css;
  139. m.id = model.id;
  140. m.data = model.data;
  141. delete resCache[model.id];
  142. return m;
  143. },
  144. afterLoadByMain = function(model, data) {
  145. showMainInternal(cacheModel(model));
  146. },
  147. afterLoadByModal = function(model, data) {
  148. showModalInternal(cacheModel(model), data);
  149. },
  150. loadModel = function(model, handler, data) {
  151. if(htmlCache[model.uri || "#"]) {
  152. model.state = 11;
  153. if(model.script) {
  154. loadModelScript(model, handler, data);
  155. } else {
  156. handler(model, data);
  157. }
  158. } else {
  159. if(cfg.mask) {
  160. util.showLoading();
  161. }
  162. model.state = 10;
  163. $.ajax({ url: model.uri, dataType: "html", type: "GET" }).done(function(hc) {
  164. model.state = 11;
  165. htmlCache[model.uri] = hc;
  166. if(cfg.mask) {
  167. util.hideLoading();
  168. }
  169. if(model.script) {
  170. loadModelScript(model, handler, data);
  171. } else {
  172. handler(model, data);
  173. }
  174. }).fail(function(jqXHR, textStatus, errorThrown) {
  175. model.state = 12;
  176. if(cfg.mask) { util.hideLoading(); }
  177. util.raise({
  178. code: "loadModelHtml_" + (textStatus || ""),
  179. msg: textStatus,
  180. detailMsg: textStatus,
  181. xhr: jqXHR,
  182. eObj: errorThrown,
  183. url: model.uri
  184. });
  185. });
  186. }
  187. },
  188. removeModelCss = function(model) {
  189. if(model.css) {
  190. var cm = cssCache[model.css];
  191. if(cm) {
  192. cm.ref = cm.ref - 1;
  193. if(!cm.ref) {
  194. var link = doc.getElementById(cm.id);
  195. if(link) link.parentNode.removeChild(link);
  196. delete cssCache[model.css];
  197. }
  198. }
  199. }
  200. },
  201. loadModelScript = function(model, handler, data) {
  202. model.factory = scriptCache[model.script];
  203. if(model.factory) {
  204. model.state = 31;
  205. handler.call(model, data);
  206. return;
  207. }
  208. var node = doc.createElement("script");
  209. node.async = true;
  210. node.src = model.script;
  211. node.charset = "UTF-8";
  212. var supportOnload = "onload" in node;
  213. if(cfg.mask) {
  214. util.showLoading();
  215. }
  216. window.spa_define = function(factoryBuilder) {
  217. model.state = 30;
  218. if(cfg.mask) {
  219. util.showLoading();
  220. }
  221. try {
  222. scriptCache[model.script] = model.factory = factoryBuilder(spa);
  223. model.state = 31;
  224. if(cfg.mask) {
  225. util.hideLoading();
  226. }
  227. } catch(err) {
  228. model.state = 32;
  229. if(cfg.mask) {
  230. util.hideLoading();
  231. }
  232. util.raise({
  233. code: "buildModelFactory_" + err.toString(),
  234. msg: err.toString() || "",
  235. detailMsg: err.toString() || "",
  236. url: model.script
  237. });
  238. }
  239. handler(model, data);
  240. };
  241. if(supportOnload) {
  242. node.onload = function() {
  243. if(model.state < 21) model.state = 21;
  244. node.onerror = null;
  245. node.onload = null;
  246. head.removeChild(node);
  247. if(cfg.mask) {
  248. util.hideLoading();
  249. }
  250. };
  251. node.onerror = function() {
  252. node.onload = null;
  253. node.onerror = null;
  254. head.removeChild(node);
  255. if(cfg.mask) {
  256. util.hideLoading();
  257. }
  258. if(model.state < 22) {
  259. model.state = 22;
  260. util.raise({
  261. code: "loadModelScript_",
  262. msg:"",
  263. detailMsg: "",
  264. url: model.script
  265. });
  266. }
  267. };
  268. } else {
  269. node.onreadystatechange = function() {
  270. if(/loaded|complete/.test(node.readyState)) {
  271. node.onreadystatechange = null;
  272. if(model.state < 21) {
  273. model.state = 21;
  274. var to = model.timeout || 1000;
  275. setTimeout(function() {
  276. if(model.state == 21) {
  277. model.state = 22;
  278. head.removeChild(node);
  279. if(cfg.mask) {
  280. util.hideLoading();
  281. }
  282. util.raise({
  283. code: "loadModelScript_timeout",
  284. msg: "",
  285. detailMsg: "",
  286. url: model.script
  287. });
  288. }
  289. }, model.timeout || 1000);
  290. } else {
  291. head.removeChild(node);
  292. if(cfg.mask) {
  293. util.hideLoading();
  294. }
  295. }
  296. }
  297. };
  298. }
  299. model.state = 20;
  300. baseElement ?
  301. head.insertBefore(node, baseElement) :
  302. head.appendChild(node);
  303. },
  304. cleanMain = function() {
  305. if(main) {
  306. removeModelCss(main);
  307. if(main.factory && main.factory.mainDestory) { main.factory.mainDestory() };
  308. mainEle.empty();
  309. main = null;
  310. }
  311. },
  312. getModel = function(id) {
  313. return modelCache[id];
  314. },
  315. showModal = function(id, data) {
  316. var model = modelCache[id];
  317. if(model) {
  318. showModalInternal(model, data);
  319. } else {
  320. model = resCache[id];
  321. if(model) {
  322. loadModel(model, afterLoadByModal, data);
  323. } else {
  324. util.raise({ code: "invalid_model", msg: "不正确的模块[" + id + "]", detailMsg: "不正确的模块[" + id + "]" });
  325. }
  326. }
  327. },
  328. showMain = function() {
  329. var id = win.location.hash;
  330. if(id && id.length > 1) {
  331. id = id.substring(1);
  332. } else return;
  333. if(main && id == main.id) return;
  334. var model = modelCache[id];
  335. if(model) {
  336. showMainInternal(model);
  337. } else {
  338. var model = resCache[id];
  339. if(model) {
  340. loadModel(model, afterLoadByMain);
  341. } else {
  342. util.raise({ code: "invalid_model", msg: "不正确的模块[" + id + "]", detailMsg: "不正确的模块[" + id + "]" });
  343. }
  344. }
  345. },
  346. getLastModalIndex = function() {
  347. return spa_modal_index;
  348. },
  349. getLastModalCtn = function() {
  350. return $(".spa-modal-index-" + spa_modal_index);
  351. },
  352. getLastModalModel = function() {
  353. var ctn = $(".spa-modal-index-" + spa_modal_index);
  354. var id = modalCtn.attr("spa-model-id");
  355. return getModel(id);
  356. },
  357. closeModal = function() {
  358. var ctn = $(".spa-modal-index-" + spa_modal_index);
  359. var id = ctn.attr("spa-model-id");
  360. var inx = util.listModalIndex();
  361. if(ctn.hasClass("layer-" + inx)) {
  362. var model = modelCache[id];
  363. if(model) {
  364. if(model.factory.modalDestory) {
  365. model.factory.modalDestory.call(this);
  366. }
  367. if(model.css) {
  368. removeModelCss(model);
  369. }
  370. --spa_modal_index;
  371. util.closeModal();
  372. }
  373. } else {
  374. util.raise({ code: "invalid_dir", msg: "can't close modal:has top layer", detailMsg: "can't close modal:has top layer" });
  375. }
  376. },
  377. build = function(config) {
  378. $.extend(cfg, config);
  379. resUri = cfg.resUri || body.attr("resource");
  380. menuUri = cfg.menuUri || body.attr("menu");
  381. mainEle = cfg.mainEle || $(".spa-main");
  382. menuEle = cfg.menuEle || $(".spa-menu");
  383. resCache = cfg.resCache || resCache;
  384. htmlCache = cfg.htmlCache || htmlCache;
  385. htmlCache["#"] = "#";
  386. if(cfg.loadEnabled) load_res();
  387. },
  388. spa = {
  389. build: build,
  390. closeModal: closeModal,
  391. getLastModalModel: getLastModalModel,
  392. getLastModalCtn: getLastModalCtn,
  393. getLastModalIndex: getLastModalIndex,
  394. showMain: showMain,
  395. showModal: showModal,
  396. mainChildren:function(selector){
  397. return mainEle.children(selector);
  398. },
  399. modalChildren:function(selector){
  400. return getLastModalCtn.children(selector);
  401. },
  402. findInMain:function(selector){
  403. return mainEle.find(selector);
  404. },
  405. findInModal:function(selector){
  406. return getLastModalCtn().find(selector);
  407. }
  408. };
  409. $.spa=spa;
  410. return spa;
  411. });