cartoonmanager.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574
  1. /*
  2. * Copyright (c) 2009 Nokia Corporation.
  3. */
  4. /**
  5. * This is run after the the web page has been loaded (possibly before rendering).
  6. * $(document).ready documented here:
  7. * http://docs.jquery.com/Tutorials:Introducing_$(document).ready()
  8. */
  9. $(document).ready(function() {
  10. try {
  11. /**
  12. *
  13. */
  14. console.debug("Init started.");
  15. var db = openDatabase("CartoonReader",
  16. "1.0",
  17. "CartoonReader example",
  18. 200000);
  19. if (!db) {
  20. alert("Failed to open the database on disk. This is probably because the version was bad or there is not enough space left in this domain's quota");
  21. }
  22. if(window.appWindow) {
  23. window.minimize = appWindow.minimize;
  24. }
  25. var platform = "";
  26. if(window.platformResolver) {
  27. platform = window.platformResolver.platform();
  28. }
  29. var elements = { window: window,
  30. document: window.document,
  31. headerContainer: $("#headerContainer"),
  32. navigation: $("#topNavigation"),
  33. cartoonList: $("#cartoonList"),
  34. newCartoonSourceSection: $("#newCartoonSourceSection"),
  35. images: $("img"),
  36. body: $("body"),
  37. html: $("html"),
  38. navLiButtons: $("nav li button"),
  39. cartoonReader: $("#cartoonReader")};
  40. var helpers = { jQueryEventProxy: new jQueryEventProxy(console),
  41. dndHelper: new DnDHelper(),
  42. cartoonDataFetcher: new CartoonDataFetcher(new FeedUpdateBroker(),
  43. new CartoonParser(),
  44. new FeedResolver())
  45. };
  46. helpers.maemoHelper = new MaemoHelper(window,
  47. helpers.jQueryEventProxy,
  48. helpers.dndHelper);
  49. window.CartoonManager = new CartoonManager(db,
  50. window,
  51. elements,
  52. helpers,
  53. platform);
  54. console.debug("Init finished.");
  55. }
  56. catch(e) {
  57. console.error(e);
  58. alert(e);
  59. }
  60. });
  61. function CartoonManager(db,
  62. applicationWindow,
  63. elements,
  64. helpers,
  65. platform) {
  66. this.C_SINGLE_CARTOON_VIEW_URL = "qrc:/html/cartoon-view.html";
  67. this.PLATFORM = platform;
  68. this.m_window = applicationWindow;
  69. this.m_helpers = helpers;
  70. this.m_elementCreator = new CartoonManager.ElementCreator(elements.document,
  71. helpers.jQueryEventProxy);
  72. this.m_view = new CartoonManager.View(elements);
  73. this.m_backend = new CartoonManagerBackend(db);
  74. var subscribers = [];
  75. subscribers.push(this.m_view);
  76. subscribers.push(this.m_backend);
  77. subscribers.push(this);
  78. this.m_model = new CartoonManager.Model(subscribers);
  79. var cartoonSourceResultAdapter = new CartoonSourceResultAdapter({ that: this.m_model,
  80. callback: this.m_model.receiveCartoonSources});
  81. var additionAdapter = new CartoonAdditionAdapter(this.m_backend,
  82. {that: this.m_model,
  83. callback: this.m_model.addCartoonSource});
  84. this.m_backend.registerGetCartoonSourcesAdapter(cartoonSourceResultAdapter);
  85. this.m_backend.registerAddCartoonSourceAdapter(additionAdapter);
  86. this.setup(this.m_view,
  87. helpers,
  88. elements,
  89. platform);
  90. this.m_backend.getCartoonSources();
  91. }
  92. CartoonManager.prototype = {
  93. setup: function(view,
  94. helpers,
  95. elements,
  96. platform) {
  97. view.hideAddCartoonSource(true);
  98. this.bindBackButton(helpers.jQueryEventProxy,
  99. elements.navigation.find("#backButton"));
  100. this.bindAddButton(helpers.jQueryEventProxy,
  101. elements.navigation.find("#addButton"));
  102. this.bindAddFeedButton(helpers.jQueryEventProxy,
  103. elements.newCartoonSourceSection.find("#addFeedButton"));
  104. this.bindCancelAddFeedButton(helpers.jQueryEventProxy,
  105. elements.newCartoonSourceSection.find("#cancelAddFeedButton"));
  106. helpers.dndHelper.disableDragging(elements.images);
  107. helpers.dndHelper.disableDragging(elements.body);
  108. helpers.dndHelper.disableDragging(elements.html);
  109. if(platform === "MAEMO") {
  110. helpers.maemoHelper.createTaskSwitcher(elements.headerContainer);
  111. helpers.maemoHelper.setFontSizeTo(elements.body, "1.25em");
  112. }
  113. },
  114. bindBackButton: function(jQueryEventProxy,
  115. backButton) {
  116. var handler = { that: this,
  117. handler: this.backButtonClicked };
  118. jQueryEventProxy.bind(backButton,
  119. "click",
  120. handler);
  121. },
  122. bindAddButton: function(jQueryEventProxy,
  123. addButton) {
  124. var handler = { that: this,
  125. handler: this.addButtonClicked };
  126. jQueryEventProxy.bind(addButton,
  127. "click",
  128. handler);
  129. },
  130. bindAddFeedButton: function(jQueryEventProxy,
  131. addFeedButton) {
  132. var handler = { that: this,
  133. handler: this.addFeedButtonClicked};
  134. jQueryEventProxy.bind(addFeedButton,
  135. "click",
  136. handler);
  137. },
  138. bindCancelAddFeedButton: function(jQueryEventProxy,
  139. cancelAddFeedButton) {
  140. var handler = { that: this,
  141. handler: this.cancelAddFeedButtonClicked};
  142. jQueryEventProxy.bind(cancelAddFeedButton,
  143. "click",
  144. handler);
  145. },
  146. backButtonClicked: function(target, event) {
  147. this.goToSingleCartoonView();
  148. },
  149. addButtonClicked: function(target, event) {
  150. var cartoonSourceState = this.m_view.getCartoonSourceHidden();
  151. this.m_view.hideAddCartoonSource(!cartoonSourceState);
  152. },
  153. addFeedButtonClicked: function(target, event) {
  154. var cartoonSourceAddState = this.m_view.getCartoonSourceAdditionState();
  155. if(cartoonSourceAddState !== 'loading') {
  156. this.m_view.changeCartoonSourceAdditionState('loading');
  157. var newCartoonSourceSection = this.m_view.getElement("newCartoonSourceSection");
  158. var newCartoonSourceURL = newCartoonSourceSection.find("#newCartoonSourceURL");
  159. var url = newCartoonSourceURL.attr("value");
  160. if(url.search(/http:\/\//) == -1) {
  161. url = "http://" + url;
  162. }
  163. this.m_helpers.cartoonDataFetcher.getDataFromURL(url, {cartoonReceiver: { that: this,
  164. callback: this.receiveCartoons,
  165. errorCallback: this.receiveCartoonParsingError},
  166. errorReceiver: { that: this,
  167. callback: this.receiveCartoonParsingError}
  168. });
  169. }
  170. },
  171. cancelAddFeedButtonClicked: function(target, event) {
  172. this.m_helpers.cartoonDataFetcher.abort();
  173. this.m_view.hideAddCartoonSource(true);
  174. },
  175. receiveCartoons: function(cartoons) {
  176. this.m_view.hideAddCartoonSource(true);
  177. this.m_model.createCartoonSource(cartoons.URL,
  178. cartoons.title);
  179. },
  180. receiveCartoonParsingError: function(URL, error) {
  181. this.m_view.displayNewCartoonError("Couldn't find cartoons from this URL: " + URL);
  182. },
  183. selectButtonClicked: function(target, event) {
  184. this.m_tempCartoonSource = this.findCartoonSourceId(target);
  185. this.m_model.setActiveCartoonSource(this.m_tempCartoonSource);
  186. },
  187. cartoonSourcesUpdated: function(cartoonSources) {
  188. for(var i=0; i < cartoonSources.length; i++) {
  189. if(cartoonSources[i].getActiveBoolean()) {
  190. var that = this;
  191. this.m_window.setTimeout(function(){ that.goToSingleCartoonView(); },
  192. 700);
  193. }
  194. }
  195. },
  196. goToSingleCartoonView: function() {
  197. var window = this.m_window;
  198. var url = this.C_SINGLE_CARTOON_VIEW_URL;
  199. if(this.PLATFORM !== "MAEMO" &&
  200. this.PLATFORM !== "SYMBIAN") {
  201. this.m_view.fadeOut("slow",
  202. function() {window.location = url; });
  203. }
  204. else if (this.PLATFORM === "SYMBIAN") {
  205. window.location = url;
  206. }
  207. else {
  208. window.location = url;
  209. }
  210. },
  211. removeButtonClicked: function(target, event) {
  212. var id = this.findCartoonSourceId(target);
  213. this.m_model.removeCartoonSource(id);
  214. },
  215. addCartoonSourceToView: function(cartoonSource) {
  216. var selectButtonClicked = { that: this,
  217. handler: this.selectButtonClicked };
  218. var removeButtonClicked = { that: this,
  219. handler: this.removeButtonClicked };
  220. var cartoonSourceLi = this.m_elementCreator.createManageableCartoonSource(cartoonSource,
  221. selectButtonClicked,
  222. removeButtonClicked);
  223. this.m_view.addCartoonSourceElement(cartoonSourceLi);
  224. },
  225. findCartoonSourceId: function(target) {
  226. var parent = $(target.parent().parent().get(0));
  227. // table > form > tr [- some tr contains hidden inputs] > td > button
  228. var idElement = parent.find("input[name=id]");
  229. var id = new Number(idElement.attr("value"));
  230. return id;
  231. },
  232. receiveModelChanges: function(changes) {
  233. if("added" in changes) {
  234. this.addCartoonSourceToView(changes.added.cartoonSource);
  235. }
  236. if("updated" in changes &&
  237. !("removed" in changes)) {
  238. if(changes.updated.cartoonSources !== undefined) {
  239. this.cartoonSourcesUpdated(changes.updated.cartoonSources);
  240. }
  241. }
  242. }
  243. };
  244. CartoonManager.Model = function(subscribers) {
  245. this.m_subscribers = subscribers;
  246. this.m_cartoonSources = {};
  247. };
  248. CartoonManager.Model.prototype = {
  249. receiveCartoonSources: function(cartoonSources) {
  250. for(var i = 0; i < cartoonSources.length; i++) {
  251. this.addCartoonSource(cartoonSources[i]);
  252. }
  253. },
  254. setActiveCartoonSource: function(id) {
  255. if(id in this.m_cartoonSources) {
  256. var oldActive = this.findActiveCartoonSource();
  257. if (id == oldActive.id) {
  258. var activeCartoonSource = this.m_cartoonSources[id];
  259. // Go directly to the single cartoon view
  260. var changes = { updated: {
  261. cartoonSources: [activeCartoonSource]
  262. }
  263. };
  264. this.informSubscribers(changes);
  265. }
  266. else {
  267. this.m_cartoonSources[oldActive.id].setActive(false);
  268. this.m_cartoonSources[id].setActive(true);
  269. var oldActiveCartoonSource = this.m_cartoonSources[oldActive.id];
  270. var activeCartoonSource = this.m_cartoonSources[id];
  271. var changes = { updated: {
  272. cartoonSources: [oldActiveCartoonSource,
  273. activeCartoonSource]
  274. }
  275. };
  276. this.informSubscribers(changes);
  277. }
  278. }
  279. },
  280. findActiveCartoonSource: function() {
  281. var foundCartoonSource = { id: -1, cartoonSource: undefined };
  282. for(var i in this.m_cartoonSources) {
  283. if(this.m_cartoonSources[i].getActiveBoolean()) {
  284. foundCartoonSource.cartoonSource = this.m_cartoonSources[i];
  285. foundCartoonSource.id = i;
  286. break;
  287. }
  288. }
  289. return foundCartoonSource;
  290. },
  291. createCartoonSource: function(url, name) {
  292. var cartoonSource = new CartoonSource();
  293. cartoonSource.setUrl(url);
  294. cartoonSource.setName(name);
  295. cartoonSource.setActive(false);
  296. var changes = { toBeAdded: {
  297. cartoonSource: cartoonSource
  298. }
  299. };
  300. this.informSubscribers(changes);
  301. },
  302. addCartoonSource: function(cartoonSource) {
  303. this.m_cartoonSources[cartoonSource.getId()] = cartoonSource;
  304. var changes = { added: {
  305. cartoonSource: cartoonSource
  306. }
  307. };
  308. this.informSubscribers(changes);
  309. },
  310. getCartoonSource: function(id) {
  311. return this.m_cartoonSources[id];
  312. },
  313. removeCartoonSource: function(id) {
  314. if(this.m_cartoonSources[id] !== undefined) {
  315. var toBeDeleted = this.m_cartoonSources[id];
  316. delete this.m_cartoonSources[id];
  317. var changes;
  318. if (toBeDeleted.getActive()) {
  319. for(var i in this.m_cartoonSources) {
  320. var firstSource = this.m_cartoonSources[i];
  321. }
  322. firstSource.setActive(true);
  323. changes = { removed: toBeDeleted,
  324. updated: {
  325. cartoonSources: [firstSource]
  326. }
  327. };
  328. }
  329. else {
  330. changes = { removed: toBeDeleted
  331. };
  332. }
  333. this.informSubscribers(changes);
  334. }
  335. },
  336. informSubscribers: function(changes) {
  337. for (var i=0; i<this.m_subscribers.length; i++) {
  338. this.m_subscribers[i].receiveModelChanges(changes);
  339. }
  340. }
  341. };
  342. CartoonManager.View = function(elements) {
  343. this.m_elements = elements;
  344. this.setup(this.m_elements);
  345. this.C_CARTOON_ACTIVE_ICON = "qrc:/images/active.png";
  346. this.C_CARTOON_INACTIVE_ICON = "qrc:/images/inactive.png";
  347. this.C_CARTOON_ACTIVITY_ERROR = "qrc:/images/error-icon.png";
  348. this.C_CARTOON_ACTIVITY_LOADING = "qrc:/images/loading-small.gif";
  349. };
  350. CartoonManager.View.prototype = {
  351. setup: function(elements) {
  352. elements.newCartoonSourceSection.hide();
  353. this.fadeIn("slow");
  354. },
  355. fadeIn: function(speed, callback) {
  356. this.m_elements.body.fadeIn(speed, callback);
  357. },
  358. fadeOut: function(speed, callback) {
  359. this.m_elements.body.fadeOut(speed, callback);
  360. },
  361. getElement: function(elementName) {
  362. return this.m_elements[elementName];
  363. },
  364. getCartoonSourceHidden: function() {
  365. var displayed = this.m_elements.newCartoonSourceSection.css('display');
  366. return (displayed === 'none');
  367. },
  368. hideAddCartoonSource: function(hide) {
  369. if(hide) {
  370. this.changeCartoonSourceAdditionState("");
  371. var input = this.m_elements.newCartoonSourceSection.find("#newCartoonSourceURL");
  372. input.attr("value", "");
  373. input.blur();
  374. this.m_elements.newCartoonSourceSection.fadeOut('slow');
  375. }
  376. else {
  377. var input = this.m_elements.newCartoonSourceSection.find("#newCartoonSourceURL");
  378. this.m_elements.newCartoonSourceSection.fadeIn('slow',
  379. function(){input.focus();});
  380. }
  381. },
  382. addCartoonSourceElement: function(element) {
  383. element.hide();
  384. this.m_elements.cartoonList.append(element);
  385. element.fadeIn('slow');
  386. },
  387. displayNewCartoonError: function(error) {
  388. var errorDiv = this.m_elements.newCartoonSourceSection.find("#newCartoonSourceError");
  389. errorDiv.append("<p>" + error + "</p>");
  390. this.changeCartoonSourceAdditionState("error");
  391. errorDiv.slideDown('slow').delay(2000).slideUp('slow', function() {$(this).empty();});
  392. },
  393. getCartoonSourceAdditionState: function() {
  394. var state = "";
  395. var addActivity = this.m_elements.newCartoonSourceSection.find("#newCartoonAddActivity");
  396. var url = addActivity.attr("src");
  397. if(url === this.C_CARTOON_ACTIVITY_ERROR) {
  398. state = "error";
  399. }
  400. if(url === this.C_CARTOON_ACTIVITY_LOADING) {
  401. state = "loading";
  402. }
  403. return state;
  404. },
  405. changeCartoonSourceAdditionState: function(state) {
  406. if(state === '') {
  407. this.changeAddActivityImageProperties("",
  408. "",
  409. true);
  410. }
  411. if(state === 'error') {
  412. this.changeAddActivityImageProperties(this.C_CARTOON_ACTIVITY_ERROR,
  413. "Error",
  414. false);
  415. }
  416. if(state === 'loading') {
  417. this.changeAddActivityImageProperties(this.C_CARTOON_ACTIVITY_LOADING,
  418. "Loading...",
  419. false);
  420. }
  421. },
  422. changeAddActivityImageProperties: function(src, alt, hide) {
  423. var addActivity = this.m_elements.newCartoonSourceSection.find("#newCartoonAddActivity");
  424. addActivity.attr("src", src);
  425. addActivity.attr("alt", alt);
  426. if(hide) {
  427. addActivity.hide('slow');
  428. }
  429. else {
  430. addActivity.show('slow');
  431. }
  432. },
  433. changeCartoonSourceActivity: function(id, activity) {
  434. var cartoonSourceLi = this.findCartoonSourceLi(id);
  435. var activeIcon = cartoonSourceLi.find(".activeIcon");
  436. if(activity) {
  437. activeIcon.attr("src", this.C_CARTOON_ACTIVE_ICON);
  438. activeIcon.attr("alt", "active");
  439. }
  440. else {
  441. activeIcon.attr("src", this.C_CARTOON_INACTIVE_ICON);
  442. activeIcon.attr("alt", "inactive");
  443. }
  444. },
  445. handleRemoved: function(removed) {
  446. var cartoonSourceLi = this.findCartoonSourceLi(removed.getId());
  447. cartoonSourceLi.hide('slow',
  448. function() { $(this).remove();});
  449. },
  450. handleUpdated: function(updated) {
  451. if(updated.cartoonSources !== undefined) {
  452. var cartoonSources = updated.cartoonSources;
  453. for(var i=0; i<cartoonSources.length; i++) {
  454. this.updateCartoonSource(cartoonSources[i]);
  455. }
  456. }
  457. },
  458. updateCartoonSource: function(cartoonSource) {
  459. this.changeCartoonSourceActivity(cartoonSource.getId(),
  460. cartoonSource.getActiveBoolean());
  461. },
  462. findCartoonSourceLi: function(id) {
  463. var idInput = this.m_elements.cartoonList.find("input[value=" +id+"]");
  464. var cartoonSourceLi = $(idInput.parents("li").get(0));
  465. return cartoonSourceLi;
  466. },
  467. receiveModelChanges: function(changes) {
  468. if("removed" in changes) {
  469. this.handleRemoved(changes.removed);
  470. }
  471. if("updated" in changes) {
  472. this.handleUpdated(changes.updated);
  473. }
  474. }
  475. };
  476. CartoonManager.ElementCreator = function(document,
  477. jQueryEventProxy) {
  478. this.m_document = document;
  479. this.m_jQueryEventProxy = jQueryEventProxy;
  480. };
  481. CartoonManager.ElementCreator.prototype = {
  482. createManageableCartoonSource: function(cartoonSource,
  483. selectHandler,
  484. removeHandler) {
  485. var html = "<li>" +
  486. "<form>" +
  487. "<table><tbody>" +
  488. "<tr>" +
  489. "<td class=\"activeLabelTd\">" +
  490. "<input type=\"hidden\" name=\"id\" value=\"\" ></input>" +
  491. "<input type=\"hidden\" name=\"url\" value=\"\" ></input>" +
  492. "<label for=\"\"><img class=\"activeIcon\" src=\"\" /></label>" +
  493. "</td>" +
  494. "<td class=\"feedButtonTd\"><button type=\"button\" class=\"feedButton\" name=\"\"></button></td>" +
  495. "<td class=\"removeButtonTd\"><button type=\"button\" class=\"removeButtonTd\" name=\"remove\"><img src=\"qrc:/images/icon_trash.png\" /></button></td>" +
  496. "</tr>" +
  497. "</tbody></table>" +
  498. "</form>" +
  499. "</li>";
  500. var cartoonSourceElement = $(html, this.m_document);
  501. var label = cartoonSourceElement.find("label");
  502. label.attr("for", "select-"+cartoonSource.getId());
  503. var activeIcon = cartoonSourceElement.find("img[class=activeIcon]");
  504. activeIcon.attr("src", (cartoonSource.getActiveBoolean() ?
  505. "qrc:/images/active.png" : "qrc:/images/inactive.png"));
  506. var idElement = cartoonSourceElement.find("input[name=id]");
  507. idElement.attr("value", cartoonSource.getId());
  508. var urlElement = cartoonSourceElement.find("input[name=url]");
  509. urlElement.attr("value", cartoonSource.getUrl());
  510. var feedButton = cartoonSourceElement.find("button[class=feedButton]");
  511. feedButton.attr("id", "select-"+cartoonSource.getId());
  512. feedButton.attr("name", cartoonSource.getId());
  513. feedButton.text(cartoonSource.getName());
  514. this.m_jQueryEventProxy.bind(feedButton,
  515. "click",
  516. selectHandler);
  517. var removeButton = cartoonSourceElement.find("button[name=remove]");
  518. this.m_jQueryEventProxy.bind(removeButton,
  519. "click",
  520. removeHandler);
  521. return cartoonSourceElement;
  522. }
  523. };
  524. function UpdateCartoonSourceAdapter(resultReceiver) {
  525. this.setResultReceiver(resultReceiver);
  526. this.m_cartoonSourceResultFiller = new CartoonSourceResultFiller();
  527. }
  528. UpdateCartoonSourceAdapter.prototype = {
  529. setResultReceiver: function(resultReceiver) {
  530. this.m_resultReceiver = resultReceiver;
  531. },
  532. getResultReceiver: function(receiver) {
  533. return this.m_resultReceiver;
  534. },
  535. receiveResults: function(tx, results) {
  536. var updated = false;
  537. if(results.rowsAffected > 0) {
  538. updated = true;
  539. }
  540. this.sendToReceiver(updated);
  541. },
  542. getReceiver: function() {
  543. var callbackObject = { that: this,
  544. callback: this.receiveResults};
  545. return callbackObject;
  546. },
  547. sendToReceiver: function(updated) {
  548. this.m_resultReceiver.callback.call(this.m_resultReceiver.that,
  549. updated);
  550. }
  551. };