PageRenderTime 53ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/sites/web/app.js

https://bitbucket.org/aswinvk28/smartpan-stock-drupal
JavaScript | 747 lines | 638 code | 72 blank | 37 comment | 35 complexity | c32c4b17949eaf4e29f80f4744f348ee MD5 | raw file
Possible License(s): LGPL-2.1
  1. var Stock = Stock || {};
  2. (function($) {
  3. $(document).ready(function() {
  4. $('#stock-symbol').click(function(event) {
  5. event.stopPropagation();
  6. event.preventDefault();
  7. });
  8. });
  9. })(window.jQuery);
  10. (function(Backbone, $, _, Stock) {
  11. 'use strict';
  12. Stock.ProfileWidgetContent = Backbone.View.extend({
  13. title: function() {
  14. return 'STOCK PROFILE';
  15. },
  16. size: function() {
  17. return [1, 1, 2, 1];
  18. },
  19. render: function() {
  20. var func = _.template(this.template);
  21. return func(this.model);
  22. },
  23. template: '<div class="row-fluid"><div class="span3 fx-large column decolumn_mini"><a class="nounderline"><%= symbol %></a></div>\n\
  24. <div class="span9"><div class="clearfix decolumn_mini">Stock Name: <%= name %></div>\n\
  25. <div class="clearfix decolumn_mini">Stock Industry: <%= industry %></div>\n\
  26. <div class="clearfix decolumn_mini">Stock Exchange: <%= exchange %></div>\n\
  27. <div class="raphael-ui-component flat-ui-font clearfix decolumn_mini">Currency: <%= currency %></div></div>',
  28. initialize: function(options) {
  29. return this;
  30. }
  31. });
  32. Stock.PriceWidgetContent = Backbone.View.extend({
  33. title: function() {
  34. return 'STOCK PRICE';
  35. },
  36. size: function() {
  37. return [1, 3, 1, 1];
  38. },
  39. render: function() {
  40. var func = _.template(this.template);
  41. return func(this.model);
  42. },
  43. template: '<span class="fx-large clearfix decolumn_mini"><%= price %></span>\n\
  44. <span class="raphael-ui-component clearfix decolumn_mini"><%= price_change %></span>',
  45. initialize: function(options) {
  46. return this;
  47. }
  48. });
  49. Stock.VolumeWidgetContent = Backbone.View.extend({
  50. title: function() {
  51. return 'STOCK VOLUME';
  52. },
  53. size: function() {
  54. return [1, 4, 1, 1];
  55. },
  56. render: function() {
  57. var func = _.template(this.template);
  58. return func(this.model);
  59. },
  60. template: '<span class="fx-large clearfix decolumn_mini"><%= volume %></span>\n\
  61. <span class="raphael-ui-component clearfix decolumn_mini"><%= volume_change %></span>',
  62. initialize: function(options) {
  63. return this;
  64. }
  65. });
  66. Stock.IndustryRankWidgetContent = Backbone.View.extend({
  67. title: function() {
  68. return 'INDUSTRY RANK';
  69. },
  70. size: function() {
  71. return [1, 5, 1, 1];
  72. },
  73. render: function() {
  74. var func = _.template(this.template);
  75. return func(this.model);
  76. },
  77. template: '<span class="fx-large clearfix decolumn_mini"><%= industry_rank %></span>',
  78. initialize: function(options) {
  79. return this;
  80. }
  81. });
  82. Stock.MarketRankWidgetContent = Backbone.View.extend({
  83. title: function() {
  84. return 'MARKET RANK';
  85. },
  86. size: function() {
  87. return [1, 6, 1, 1];
  88. },
  89. render: function() {
  90. var func = _.template(this.template);
  91. return func(this.model);
  92. },
  93. template: '<span class="fx-large clearfix decolumn_mini"><%= market_rank %></span>',
  94. initialize: function(options) {
  95. return this;
  96. }
  97. });
  98. Stock.ReturnWidgetContent = Backbone.View.extend({
  99. title: function() {
  100. return 'STOCK RETURN';
  101. },
  102. size: function() {
  103. return [2, 1, 1, 2];
  104. },
  105. render: function() {
  106. var func = _.template(this.template);
  107. return func({
  108. week: this.model['week'] ? this.model['week'].price : null,
  109. quarter: this.model['quarter'] ? this.model['quarter'].price : null,
  110. half: this.model['half'] ? this.model['half'].price : null,
  111. year: this.model['year'] ? this.model['year'].price : null,
  112. });
  113. },
  114. template: '<span class="fx-large clearfix decolumn_mini"><%= week %></span>\n\
  115. <span class="fx-large clearfix decolumn_mini"><%= quarter %></span>\n\
  116. <span class="fx-large clearfix decolumn_mini"><%= half %></span>\n\
  117. <span class="fx-large clearfix decolumn_mini"><%= year %></span>',
  118. initialize: function(options) {
  119. return this;
  120. }
  121. });
  122. Stock.InvestorWidgetContent = Backbone.View.extend({
  123. title: function() {
  124. return 'STOCK INVESTORS';
  125. },
  126. size: function() {
  127. return [2, 2, 4, 1];
  128. },
  129. render: function() {
  130. var func = _.template(this.template);
  131. return func(this.model);
  132. },
  133. template: '<span class="raphael-ui-component"><%= type1 %></span>\n\
  134. <span class="raphael-ui-component"><%= type2 %></span>\n\
  135. <span class="raphael-ui-component"><%= type3 %></span>\n\
  136. <span class="raphael-ui-component"><%= type4 %></span>\n\
  137. <span class="raphael-ui-component"><%= type5 %></span>\n\
  138. <span class="raphael-ui-component"><%= type6 %></span>\n\
  139. <span class="raphael-ui-component"><%= type7 %></span>\n\
  140. <span class="raphael-ui-component"><%= type8 %></span>\n\
  141. <span class="raphael-ui-component"><%= type9 %></span>',
  142. initialize: function(options) {
  143. return this;
  144. }
  145. });
  146. Stock.NewsWidgetContent = Backbone.View.extend({
  147. title: function() {
  148. return 'STOCK NEWS';
  149. },
  150. size: function() {
  151. return [3, 2, 2, 2];
  152. },
  153. render: function() {
  154. var func = _.template(this.template);
  155. return func(this.mdoel);
  156. },
  157. template: '<span class="fx-large clearfix"><%= market_rank %></span>',
  158. initialize: function(options) {
  159. return this;
  160. }
  161. });
  162. Stock.WidgetView = Backbone.View.extend({
  163. tagName: 'ul',
  164. className: 'gridster-container white span12',
  165. maxColumn: 6,
  166. widgets: function() {
  167. return [
  168. {'price':'PriceWidgetContent'},{'volume':'VolumeWidgetContent'},
  169. {'industry':'IndustryRankWidgetContent'},{'market':'MarketRankWidgetContent'},
  170. {'profile':'ProfileWidgetContent'},{'return':'ReturnWidgetContent'},
  171. {'news':'NewsWidgetContent'},{'investor': 'InvestorWidgetContent'}
  172. ];
  173. },
  174. render: function() {
  175. var func = null;
  176. var view = null;
  177. var widgetHtml = null;
  178. var size = null;
  179. var class_name = null;
  180. var positions = null;
  181. var views = _.values(this.collection.views);
  182. var widgetTypes = _.keys(this.collection.views);
  183. _.each(views, function(element, index) {
  184. if(element.view && element.model) {
  185. func = _.template($('script#widget-view').html());
  186. view = new element.view({
  187. model: element.model
  188. });
  189. size = view.size();
  190. class_name = this.calculateDimensions(size);
  191. class_name = class_name.join(' ');
  192. widgetHtml = func({
  193. widget_row: size[0],
  194. widget_col: size[1],
  195. widget_sizex: size[2],
  196. widget_sizey: size[3],
  197. widget_title: view.title(),
  198. widget_content: view.render(),
  199. class_name: class_name
  200. });
  201. $(this.el).append(widgetHtml);
  202. }
  203. }, this);
  204. this.setContent();
  205. },
  206. calculateDimensions: function(size) {
  207. var x = size[2];
  208. var y = size[3];
  209. var class_name = [];
  210. switch(x) {
  211. case 1:
  212. class_name.push('span2');
  213. break;
  214. case 2:
  215. class_name.push('span5');
  216. break;
  217. case 3:
  218. class_name.push('span7');
  219. break;
  220. case 4:
  221. class_name.push('span10');
  222. break;
  223. default: break;
  224. }
  225. switch(y) {
  226. case 1:
  227. class_name.push('vertical-span2');
  228. break;
  229. case 2:
  230. class_name.push('vertical-span5');
  231. break;
  232. case 3:
  233. class_name.push('vertical-span7');
  234. break;
  235. case 4:
  236. class_name.push('vertical-span10');
  237. break;
  238. default: break;
  239. }
  240. return class_name;
  241. },
  242. initialize: function() {
  243. _.each(this.widgets(), this.collection.addView, this.collection);
  244. return this;
  245. },
  246. setContent: function() {
  247. $('#page-gridster').append(this.el);
  248. var verticalSpan = $(this.el).find('.vertical-span2').first();
  249. var horizontalSpan = $(this.el).find('.span2').first();
  250. var $widget_margin_horiz = ( verticalSpan.outerHeight(true) - verticalSpan.outerHeight() ) / 2;
  251. var $widget_base_width = horizontalSpan.width();
  252. var $widget_base_height = horizontalSpan.height();
  253. if(!!$.fn.gridster) {
  254. $(this.el).gridster({
  255. widget_margins: [$widget_margin_horiz, $widget_margin_horiz],
  256. widget_base_dimensions: [$widget_base_width, $widget_base_height]
  257. });
  258. }
  259. }
  260. });
  261. Stock.WidgetCollection = Backbone.Collection.extend({
  262. initialize: function() {
  263. this.views = {};
  264. },
  265. addView: function(element, index) {
  266. var values = _.values(element);
  267. var keys = _.keys(element);
  268. this.views[keys[0]] = {'view': Stock[values[0]]};
  269. },
  270. addItem: function(element, index) {
  271. var model = element.toJSON();
  272. var values = _.values(model);
  273. var keys = _.keys(model);
  274. this.views[keys[0]]['model'] = values[0];
  275. },
  276. getViews: function() {
  277. return this.views;
  278. }
  279. });
  280. Stock.DashboardView = Backbone.View.extend({
  281. widgetView: new Stock.WidgetView({
  282. collection: new Stock.WidgetCollection()
  283. }),
  284. initialize: function(options) {
  285. this.options = options;
  286. return this;
  287. },
  288. render: function() {
  289. this.collection.url = this.collection.url.replace('{symbol}/{id}', this.model.symbol() + '/' + this.model.get('id'));
  290. this.execute();
  291. },
  292. execute: function() {
  293. var view = this;
  294. var callbacks = execute.apply(this);
  295. var success = callbacks.success;
  296. var promise = _.extend({}, callbacks);
  297. promise.success = function(collection, response) {
  298. success(collection, response);
  299. view.loadResult(collection);
  300. };
  301. this.collection.fetch(promise);
  302. },
  303. loadResult: function(collection) {
  304. if (_.size(collection)) {
  305. collection.each(this.widgetView.collection.addItem, this.widgetView.collection);
  306. }
  307. this.widgetView.render();
  308. }
  309. });
  310. var Collection = {},
  311. Workspace = Workspace || {},
  312. latestStock = function(symbol) {
  313. return new Stock.StockData({
  314. url: "http://query.yahooapis.com/v1/public/yql?q=SELECT%20*%20from%20yahoo.finance.quoteslist%20where%20symbol%20%3D%20'" + symbol + "'&format=json&diagnostics=false&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys&callback="
  315. });
  316. },
  317. escape = function(namespace) {
  318. var that = this;
  319. if (this.isShown) {
  320. $(document).on('keyup', function ( e ) {
  321. e.which == 27 && that.hide()
  322. });
  323. $(document).on('click.symbol', function ( e ) {
  324. that.hide()
  325. });
  326. } else if (!this.isShown) {
  327. $(document).off('keyup');
  328. $(document).off('click.symbol');
  329. }
  330. },
  331. historicalSTock = function(symbol, startDate, endDate) {
  332. return new Stock.StockData({
  333. url: "http://query.yahooapis.com/v1/public/yql?q=SELECT%20*%20from%20yahoo.finance.historicaldata%20where%20symbol%20%3D%20'" + symbol + "'%20and%20startDate='" + startDate + "'%20andendDate='" + endDate + "'&format=json&diagnostics=false&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys&callback="
  334. });
  335. },
  336. BootstrapAlert = Backbone.View.extend({
  337. initialize: function(options) {
  338. var view = this;
  339. $(this.el).html(view.render({
  340. message: options.message,
  341. type: options.type
  342. }));
  343. return this;
  344. },
  345. tagName: 'div',
  346. className: 'alert alert-info clearfix column_small decolumn_small',
  347. template: '<button type="button" class="close" data-dismiss="alert">&times;</button><strong><%= type %>!</strong> <%= message %>',
  348. render: function(options) {
  349. var func = _.template(this.template);
  350. return func(options);
  351. }
  352. }),
  353. BootstrapAlertPreLoaded = Backbone.View.extend({
  354. initialize: function(options) {
  355. var view = this;
  356. $(this.el).html(options.message);
  357. return this;
  358. },
  359. tagName: 'div',
  360. className: 'clearfix'
  361. }),
  362. common = {
  363. error: function(message) {
  364. var bootstrapAlert = new BootstrapAlert({
  365. message : message ? message : 'Request Could not be satisfied',
  366. type: 'Information'
  367. });
  368. $('.status-messages').html(bootstrapAlert.el).show();
  369. },
  370. errorPreloaded: function(message) {
  371. var bootstrapAlert = new BootstrapAlertPreLoaded({
  372. message : message
  373. });
  374. $('.status-messages').html(bootstrapAlert.el).show();
  375. }
  376. },
  377. execute = function() {
  378. var context = this;
  379. var object = {
  380. success: function(collection, response) {
  381. Collection.fetchData.call(context, collection, response);
  382. },
  383. error: function(xhr, textStatus, errorThrown) {
  384. if(!xhr.responseJSON) {
  385. common.error();
  386. } else {
  387. Collection.loadError(xhr);
  388. }
  389. return this;
  390. },
  391. complete: function(xhr, textStatus) {
  392. }
  393. };
  394. return object;
  395. };
  396. Workspace.AutoCompleteItemView = Backbone.View.extend({
  397. tagName: "li",
  398. template: _.template('<a href="#" data-stock-id="<%= id %>" data-stock-symbol="<%= symbol %>"><%= symbol %></a>'),
  399. events: {
  400. "click": "select"
  401. },
  402. initialize: function(options) {
  403. this.options = options;
  404. },
  405. render: function () {
  406. this.$el.html(this.template({
  407. "id": this.model.get('id'),
  408. "symbol": this.model.symbol()
  409. }));
  410. return this;
  411. },
  412. select: function () {
  413. this.options.parent.hide().select(this.model);
  414. return false;
  415. }
  416. });
  417. Workspace.StockChoice = Backbone.Model.extend({
  418. symbol: function() {
  419. return this.get("symbol");
  420. },
  421. label: function() {
  422. return this.symbol();
  423. }
  424. });
  425. Workspace.StockChoiceList = Backbone.Collection.extend({
  426. model: Workspace.StockChoice,
  427. url: "/symbol",
  428. initialize: function(options) {
  429. this.fetched = 0;
  430. return this;
  431. }
  432. });
  433. Stock.BASE_URI = 'http://query.yahooapis.com/v1/public/yql?';
  434. Stock.ENV_PARAM = "store%3A%2F%2Fdatatables.org%2Falltableswithkeys";
  435. Stock.FORMAT = 'json';
  436. Stock.PARAMS = function(options) {
  437. var context = this;
  438. var object = {
  439. q: options.query,
  440. diagnostics: context.diagnostics,
  441. env: Stock.ENV_PARAM,
  442. format: Stock.FORMAT
  443. };
  444. return object;
  445. };
  446. Collection.fetchData = function(collection, response) {
  447. this.fetched++;
  448. if(!collection) {
  449. common.error();
  450. }
  451. return this;
  452. };
  453. Collection.loadError = function(responseObject) {
  454. if(_.size(responseObject.responseJSON)) {
  455. common.errorPreloaded(responseObject.responseJSON[0].data);
  456. } else if(responseObject.responseText) {
  457. var json = JSON.parse(responseObject.responseText);
  458. common.errorPreloaded(json[0].data);
  459. }
  460. };
  461. // Stock.toQueryString = function(obj) {
  462. // var parts = [];
  463. // for(var each in obj) if (obj.hasOwnProperty(each)) {
  464. // parts.push(encodeURIComponent(each) + '=' + encodeURIComponent(obj[each]));
  465. // }
  466. // return Stock.BASE_URI + parts.join('&');
  467. // };
  468. //
  469. // Stock.StockData = Backbone.Collection.extend({
  470. // url: null,
  471. // diagnostics: false,
  472. // urlGenerator: function(options) {
  473. // this.url = options.url || Stock.toQueryString(Stock.PARAMS.call(this, options));
  474. // },
  475. // initialize: function(options) {
  476. // this.urlGenerator(options);
  477. // this.fetched = 0;
  478. // this.fetch(execute.apply(this));
  479. // return this;
  480. // },
  481. // errorPage: common.errorPage
  482. // });
  483. // Stock.StockPriceCollection = Backbone.Collection.extend({
  484. // sync: Backbone.localforage.sync('Stock.StockPriceCollection'),
  485. // initialize: function(options) {
  486. //
  487. // }
  488. // });
  489. //
  490. // Stock.StockVolumeCollection = Backbone.Collection.extend({
  491. // sync: Backbone.localforage.sync('Stock.StockVolumeCollection'),
  492. // initialize: function(options) {
  493. //
  494. // }
  495. // });
  496. Stock.StockSymbolData = Backbone.Collection.extend({
  497. url: "/stock/{symbol}/{id}",
  498. initialize: function() {
  499. }
  500. });
  501. Stock.StockSymbolInput = Backbone.Model.extend({
  502. stock_symbol: null,
  503. changeStockSymbol: function(model, view, dashboard) {
  504. var attr = $(view.el).data('action');
  505. var value = model.symbol() + '/' + model.get('id');
  506. $(view.el).attr('action', attr[attr.length - 1] === '/' ? attr + value : attr + '/' + value);
  507. $('input[name="stock_id"]').val(model.get('id'));
  508. new dashboard({
  509. collection: new Stock.StockSymbolData(),
  510. model: model
  511. }).render();
  512. },
  513. initialize: function() {
  514. this.on('change:stock_symbol', function(model, value, options) {
  515. model.changeStockSymbol(value, options.view, options.dashboard);
  516. });
  517. }
  518. });
  519. Workspace.RaphaelDOMWrapper = Backbone.View.extend({
  520. initialize: function(options) {
  521. this.raphaelDOM = options.dom;
  522. $(this.el).append(this.raphaelDOM);
  523. return this;
  524. }
  525. });
  526. (function(Backbone, $, _) {
  527. var stockChoiceList = new Workspace.StockChoiceList();
  528. var stockInputModel = new Stock.StockSymbolInput();
  529. Stock.StockInputFormView = Backbone.View.extend({
  530. model: stockInputModel,
  531. initialize: function(options) {
  532. this.input = $(this.el).find('#stock-symbol');
  533. }
  534. });
  535. Workspace.AutoCompleteView = Backbone.View.extend({
  536. tagName: "ul",
  537. className: "autocomplete btn-success",
  538. wait: 300,
  539. queryParameter: "symbol",
  540. minKeywordLength: 1,
  541. currentText: "",
  542. itemView: Workspace.AutoCompleteItemView,
  543. formView: new Stock.StockInputFormView({
  544. el: $('#stock-symbol').parents('form.form-search').first()
  545. }),
  546. dashboardView: Stock.DashboardView,
  547. initialize: function (options) {
  548. _.extend(this, options);
  549. this.filter = _.debounce(this.filter, this.wait);
  550. },
  551. render: function () {
  552. // disable the native auto complete functionality
  553. this.input.attr("autocomplete", "off");
  554. this.$el.width(this.input.outerWidth());
  555. this.input
  556. .keyup(_.bind(this.keyup, this))
  557. .keydown(_.bind(this.keydown, this))
  558. .after(this.$el);
  559. return this;
  560. },
  561. keydown: function (event) {
  562. if (event.keyCode == 38) return this.move(-1);
  563. if (event.keyCode == 40) return this.move(+1);
  564. if (event.keyCode == 13) return this.onEnter();
  565. if (event.keyCode == 27) return this.hide();
  566. },
  567. keyup: function () {
  568. var keyword = this.input.val();
  569. if (this.isChanged(keyword)) {
  570. if (this.isValid(keyword)) {
  571. this.filter(keyword);
  572. } else {
  573. this.hide()
  574. }
  575. }
  576. },
  577. filter: function (keyword) {
  578. if (this.model.url) {
  579. var parameters = {};
  580. var view = this;
  581. parameters[this.queryParameter] = keyword;
  582. var callbacks = execute.apply(this);
  583. var success = callbacks.success;
  584. var promise = _.extend({}, callbacks);
  585. promise.success = function(collection, response) {
  586. success(collection, response);
  587. view.loadResult(collection, keyword);
  588. };
  589. promise.data = parameters;
  590. Backbone.ajax(_.extend({
  591. 'type': 'POST',
  592. 'url': this.model.url
  593. }, promise));
  594. } else {
  595. this.loadResult(this.model.filter(function (model) {
  596. return model.label().toLowerCase().indexOf(keyword) !== -1
  597. }), keyword);
  598. }
  599. },
  600. isValid: function (keyword) {
  601. return keyword.length > this.minKeywordLength
  602. },
  603. isChanged: function (keyword) {
  604. return this.currentText != keyword;
  605. },
  606. move: function (position) {
  607. var current = this.$el.children(".active"),
  608. siblings = this.$el.children(),
  609. index = current.index() + position;
  610. if (siblings.eq(index).length) {
  611. current.removeClass("active");
  612. siblings.eq(index).addClass("active");
  613. }
  614. return false;
  615. },
  616. onEnter: function () {
  617. this.$el.children(".active").click();
  618. return false;
  619. },
  620. loadResult: function (model, keyword) {
  621. this.currentText = keyword;
  622. this.show().reset();
  623. if (model && model.length) {
  624. _.forEach(model, this.addItem, this);
  625. this.show();
  626. } else {
  627. this.hide();
  628. common.error();
  629. }
  630. },
  631. addItem: function (model) {
  632. this.$el.append(new this.itemView({
  633. model: new Workspace.StockChoice(model),
  634. parent: this
  635. }).render().$el);
  636. },
  637. select: function (model) {
  638. var label = model.label();
  639. this.input.val(label);
  640. this.currentText = label;
  641. this.onSelect(model);
  642. },
  643. reset: function () {
  644. this.$el.empty();
  645. return this;
  646. },
  647. hide: function () {
  648. this.isShown = false;
  649. escape.call(this);
  650. this.$el.hide();
  651. return this;
  652. },
  653. show: function () {
  654. this.isShown = true;
  655. escape.call(this);
  656. this.$el.show();
  657. return this;
  658. },
  659. // callback definitions
  660. onSelect: function (model) {
  661. var value = $(this.el).val();
  662. this.modelBind.set('stock_symbol', model, {
  663. silent: false,
  664. view: this.formView,
  665. dashboard: this.dashboardView
  666. });
  667. }
  668. });
  669. new Workspace.AutoCompleteView({
  670. input: $("#stock-symbol"),
  671. model: stockChoiceList,
  672. modelBind: stockInputModel
  673. }).render();
  674. })(window.Backbone, window.jQuery, window._);
  675. })(window.Backbone, window.jQuery, window._, Stock);