PageRenderTime 49ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 1ms

/static/js/views/drawerviews.js

https://bitbucket.org/gordonbrander/accordion-drawer-prototypes
JavaScript | 257 lines | 154 code | 50 blank | 53 comment | 7 complexity | 4d8ab1a0ed904a06de1fb294ed08652e MD5 | raw file
Possible License(s): Apache-2.0
  1. define([
  2. 'underscore',
  3. 'backbone',
  4. 'views/singleview',
  5. 'views/listview',
  6. 'models/stackcollection',
  7. 'models/stackmodel',
  8. 'models/sitemodel',
  9. 'lib/template'
  10. ], function (
  11. util,
  12. Backbone,
  13. SingleView,
  14. ListView,
  15. StackCollection,
  16. StackModel,
  17. SiteModel,
  18. template
  19. ) {
  20. // Define shorthands for prototypes. This allows easy calls to `super` methods.
  21. var __SingleView = SingleView.prototype;
  22. var __ListView = ListView.prototype;
  23. // Place
  24. // ---------------------------------------------------------------------------
  25. var PlaceView = SingleView.extend({
  26. model: SiteModel,
  27. // Make `PlaceView` a list item. It lives in an EraView list view.
  28. tagName: 'li',
  29. className: 'accordion--item',
  30. template: template('<a class="place link {{selected}}" href="{place_url}"><span class="media media--ico"></span><span class="title">{place_title}</span></a>'),
  31. events: {
  32. 'click .link': 'onClick'
  33. },
  34. onClick: function (e) {
  35. e.preventDefault();
  36. e.stopPropagation();
  37. var model = this.model;
  38. // Capture the place collection from the model's collection reference.
  39. var prevPlaceCollection = model.collection;
  40. // Capture the stack reference from the placeCollection.
  41. var stack = prevPlaceCollection.stack();
  42. var nextPlaceCollection = stack.eras().at(0).places();
  43. // Update the accessed time so the model will be correctly sorted.
  44. model.set({
  45. accessed: Date.now()
  46. });
  47. // If the previous place collection is not the next one (in other words
  48. // if we're moving a place between a collection), we'll have to do some
  49. // hand-off work.
  50. if (prevPlaceCollection !== nextPlaceCollection) {
  51. prevPlaceCollection.remove(model);
  52. // Traverse back down the chain:
  53. //
  54. // 1. Grab the eras for the stack
  55. // 2. Get the first era (model)
  56. // 3. Get the places (collection) for that era
  57. // 4. Append the model to the places -- it will be put at the top because
  58. // of the comparitor on `PlaceCollection`.
  59. nextPlaceCollection.add(model);
  60. }
  61. // Otherwise, this is a simple move operation.
  62. else {
  63. prevPlaceCollection.move(model, { at: 0 });
  64. }
  65. nextPlaceCollection.selectPlace(model.id);
  66. },
  67. render: function () {
  68. var model = this.model;
  69. var html = this.template({
  70. place_title: model.get('place_title'),
  71. place_url: model.get('place_url'),
  72. selected: model.get('selected') ? 'activated' : ''
  73. });
  74. $(this.el).html(html);
  75. return this;
  76. }
  77. });
  78. // Shared static property.
  79. PlaceView.highestZIndex = 1;
  80. // Era
  81. // ---------------------------------------------------------------------------
  82. //
  83. // Handles rendering the styles for an "era" -- a time-span of places.
  84. // Era is a listview, but itself lives inside of a listview.
  85. var EraView = ListView.extend({
  86. view: PlaceView,
  87. tagName: 'li',
  88. className: 'era',
  89. attachPoints: {
  90. 'list': '.accordion--items'
  91. },
  92. template: template('<span class="era--title title">{era_title}</span><ul class="accordion--items"></ul>'),
  93. initialize: function (options) {
  94. // Requires a stackModel, or another model that implements `places`.
  95. this.collection = this.model.places();
  96. __ListView.initialize.call(this, options);
  97. },
  98. // Calculate the height of this element.
  99. // Does a reduce on the height of the `era` views inside, which in turn
  100. // does a reduce on the `height` of the individual places.
  101. // Returns the memoized height, unless recalculate is true.
  102. height: function (recalculate) {
  103. // If we have a memoized height, and we're not asked to recalculate,
  104. // return the memoized height.
  105. if (this._height !== undefined && !recalculate) return this._height;
  106. // Calculate the sum total height of all the places.
  107. var placesHeight = util.reduce(this.views(), function (memo, place) {
  108. return memo + $(place.render().el).height();
  109. }, 0);
  110. // Get the height of the era title.
  111. var titleHeight = this.$('.era--title').height();
  112. // Add them together, memoized the result and return it.
  113. return this._height = placesHeight + titleHeight;
  114. },
  115. _repositionViews: function () {
  116. // Do a reduce operation on the sub-views, recalulating where they should
  117. // be placed within this era. `memo` is the sum total height of the
  118. // views before the current view.
  119. util.reduce(this.views(), function (memo, place) {
  120. var $el = $(place.el);
  121. $el.css('top', memo + 'px');
  122. return memo + $el.height();
  123. }, 0);
  124. return this;
  125. },
  126. moveView: function (model, options) {
  127. var view = this.lookup(model);
  128. // Set this view as the highest index (one higher than all other places).
  129. // This will ensure it animates *over* other places.
  130. $(view.el).css('z-index', PlaceView.highestZIndex++);
  131. // Wait for move animation to finish and then move in DOM for reals.
  132. __ListView.moveView.call(this, model, options);
  133. },
  134. render: function () {
  135. // Recalculate the height of this element, and update the memoized
  136. // property.
  137. var height = this._height = this.height(true);
  138. $(this.el).css('height', (this.collection.length ? height : 0) + 'px');
  139. this._repositionViews();
  140. return this;
  141. }
  142. });
  143. // Stack
  144. // ---------------------------------------------------------------------------
  145. //
  146. // Handles rendering the styles for a stack containing eras.
  147. // StackView is a listview, but itself lives inside of a listview.
  148. var StackView = ListView.extend({
  149. view: EraView,
  150. tagName: 'li',
  151. className: 'accordion--group',
  152. attachPoints: {
  153. 'list': '.eras'
  154. },
  155. events: {
  156. 'click .link': 'onClick'
  157. },
  158. template: template('<a class="stack link"><span class="media media--ico"></span><span class="title">{stack_title}</span></a><ul class="eras"></ul>'),
  159. initialize: function (options) {
  160. // Requires a stackModel, or another model that implements `eras`.
  161. this.collection = this.model.eras();
  162. __ListView.initialize.call(this, options);
  163. this.model
  164. .bind('change', this.render, this)
  165. .bind('eras add', this.render, this)
  166. .bind('eras remove', this.render, this)
  167. .bind('places add', this.render, this)
  168. .bind('places remove', this.render, this);
  169. },
  170. onClick: function (e) {
  171. e.preventDefault();
  172. var stackModel = this.model;
  173. stackModel.collection.each(function (model) {
  174. if (model !== stackModel) model.set({ open: false });
  175. });
  176. stackModel.set({ open: !this.model.get('open') });
  177. },
  178. // Calculate the height of this element.
  179. // Does a reduce on the height of the `era` views inside, which in turn
  180. // does a reduce on the `height` of the individual places.
  181. height: function (recalculate) {
  182. // If we have a memoized height, and we're not asked to recalculate,
  183. // return the memoized height.
  184. if (this._height !== undefined && !recalculate) return this._height;
  185. return this._height = util.reduce(this.views(), function (memo, era) {
  186. return memo + era.render().height();
  187. }, 0);
  188. },
  189. render: function () {
  190. var model = this.model;
  191. var totalHeight = this.height(true);
  192. this.$('list').css('height', (model.get('open') ? totalHeight : 0) + 'px');
  193. return this;
  194. }
  195. });
  196. var StackListView = ListView.extend({
  197. view: StackView,
  198. tagName: 'ul',
  199. className: 'accordion'
  200. });
  201. return {
  202. PlaceView: PlaceView,
  203. EraView: EraView,
  204. StackView: StackView,
  205. StackListView: StackListView
  206. };
  207. });