PlacesView.js 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. /**
  2. * PlacesView.js defines the view object of the applications main view
  3. *
  4. * Places view lists available places nearby
  5. * Note! We are using this same view also when user has selected
  6. * some category; when category is selected, the category name is
  7. * displayed instead of default text 'Nearby places'. Also
  8. * back button behaviour is different (back goes to categories)
  9. *
  10. */
  11. qype.views.PlacesView = Class.create(View, {
  12. initialize: function($super, id) {
  13. $super(id);
  14. this.currentCategoryName = undefined;
  15. /* ***************************
  16. PLACE OF CODE SNIPPET 10.1
  17. ****************************/
  18. // Create new snippet for the link leading to CategoriesView and bind
  19. // it to a DOM node.
  20. var categoriesLink = new Snippet("categoriesLink");
  21. /* ***************************
  22. PLACE OF CODE SNIPPET 10.2
  23. ****************************/
  24. // Add default template
  25. categoriesLink.onDefault.template.push(qype.str.places_link_to_categories + " >>");
  26. /* ***************************
  27. PLACE OF CODE SNIPPET 10.3
  28. ****************************/
  29. // Event handlers can be attached directly to the DOM node
  30. var goToCategories = function() {
  31. this.engine.activateView("categoriesView");
  32. }
  33. categoriesLink.dom.addEventListener("click", goToCategories.bind(this), false);
  34. // Create new snippet for the header and bind to a DOM node.
  35. var header = new Snippet("placesHeader");
  36. // Add default template
  37. header.onDefault.template.push({
  38. fn: function() { // Gives the appropriate header string for the view
  39. return this.currentCategoryName || qype.str.places_header_nearby
  40. },
  41. base: this
  42. });
  43. /* ***************************
  44. PLACE OF CODE SNIPPET 11.1
  45. ****************************/
  46. // Create new snippet for the actual content and bind to a DOM node.
  47. var content = new Snippet("placesContent");
  48. // Add default template
  49. content.onDefault.template.push("<div class='progress'></div>");
  50. // Different rendering when there's required data available
  51. content.onData.template.push({
  52. fn: this.constructPlacesHtml,
  53. base: this
  54. });
  55. // When to actually use the onData templates?
  56. content.onData.doUse = function() {
  57. return content.data.places !== undefined;
  58. };
  59. // And finally register all the Snippets to this view
  60. this.registerSnippets({
  61. categoriesLink: categoriesLink,
  62. header: header
  63. /* ***************************
  64. PLACE OF CODE SNIPPET 11.2
  65. ****************************/
  66. ,
  67. content: content
  68. });
  69. // Register global subscribes
  70. this.registerSubscribes([
  71. {
  72. eventName: "placeClicked",
  73. callback: this.onItemClick,
  74. context: this
  75. },
  76. {
  77. eventName: "mapLinkClicked",
  78. callback: this.onMapLinkClick,
  79. context: this
  80. },
  81. {
  82. eventName: "locationButtonClicked",
  83. callback: this.onLocationButtonClick,
  84. context: this
  85. }
  86. ]);
  87. },
  88. // Templates help separating the JS values and actual HTML content. The templates
  89. // of Prototype.js are used, even though they can handle only templating of
  90. // simple data types and not e.g. JS Objects in JSON.
  91. templates: {
  92. place: new Template(
  93. '<div class="item" onclick="EventManager.publish({name:\'#{eventName}\', id:\'#{id}\', distance:\'#{distance}\'})">' +
  94. ' <div class="image">#{displayImage}</div>' +
  95. ' <div class="info">' +
  96. ' <div class="name">#{title}</div>' +
  97. ' <div class="rating">#{rating}</div>' +
  98. ' <div class="category">#{categoryName}</div>' +
  99. ' </div>' +
  100. ' <div class="distance">#{distance}</div>' +
  101. '</div>'
  102. ),
  103. empty: new Template(
  104. '<div class="item empty">' +
  105. ' <div class="name">#{label}</div>' +
  106. '</div>'
  107. )
  108. },
  109. /**
  110. * Override the default build to prepare the view as needed.
  111. */
  112. build: function($super, params) {
  113. $super(params);
  114. /* ***************************
  115. PLACE OF CODE SNIPPET 11.3
  116. ****************************/
  117. // Remove the old data and update the snippet. Thus, old content is not shown
  118. // while retrieving new data, instead the default progress spinner is shown.
  119. this.snippets.content.data.places = undefined;
  120. this.snippets.content.rewrite();
  121. // Params allow showing one single category.
  122. if(params && params.categoryId && params.categoryName) {
  123. // If we opened this view via CategoriesView, then do not show the
  124. // 'Categories' shortcut on the top.
  125. this.snippets.categoriesLink.dom.hide();
  126. this.currentCategoryId = params.categoryId;
  127. this.currentCategoryName = params.categoryName;
  128. }
  129. else {
  130. // By default a shortcut to the CategoriesView is shown.
  131. this.snippets.categoriesLink.dom.show();
  132. this.currentCategoryId = undefined;
  133. this.currentCategoryName = undefined;
  134. }
  135. // Start retrieving the data
  136. this.getPlaces();
  137. },
  138. /**
  139. * @param {Object} evt Holds the event as it was published via the EventManager.
  140. * See the templates.place from this class.
  141. */
  142. onItemClick: function(evt) {
  143. util.log("{PlacesView.onItemClick} start");
  144. this.engine.activateView("placeView", {
  145. placeid: evt.id,
  146. distance: evt.distance
  147. });
  148. },
  149. // Click handlers take care of click events in the view
  150. onMapLinkClick: function() {
  151. console.log("hmm",this.snippets.content.data.places);
  152. this.engine.activateView("mapView",{placedata:this.snippets.content.data.places});
  153. },
  154. onLocationButtonClick: function() {
  155. console.log("Refresh location clicked");
  156. // This launches GPS location request; the functionality is
  157. // defined in app.js
  158. qype.refreshLocation();
  159. },
  160. /**
  161. * Create the actual content HTML for the retrieved places. Is triggered only
  162. * if the onData templates are used, see the initialize() method.
  163. *
  164. * @return The generated HTML as a string
  165. */
  166. constructPlacesHtml: function() {
  167. /* *******************************
  168. PLACE OF CODE SNIPPET 12.4.1
  169. ********************************/
  170. var places = undefined;
  171. if(places = this.snippets.content.data.places) { // Check if there's proper data stored
  172. places = places.results;
  173. var t = this.templates.place; // Set the HTML template
  174. var arr = [];
  175. var lang = this.engine.getContentLang(); // Get the language set in Engine.js
  176. for(var i = 0; i < places.length; i++) { // loop through the list of places
  177. /* *******************************
  178. PLACE OF CODE SNIPPET 12.4.2
  179. ********************************/
  180. var place = new qype.models.PlaceModel(places[i].place, lang); // initialize new PlaceModel object (see PlaceModel.js)
  181. var data = { // Fill template variables by using the place model
  182. id: place.getId(),
  183. title: place.data.title,
  184. eventName: "placeClicked",
  185. categoryName: place.getCategoriesStr(),
  186. distance: place.getDistanceStr(),
  187. rating: '<div class="stars'+place.data.average_rating+'"></div>' + place.getReviewsStr()
  188. }
  189. var placeImage = place.getImageUrl("medium");
  190. if(placeImage) {
  191. data.displayImage = '<div style="background-image: url(\''+placeImage+'\');" class="listItem"></div>';
  192. }
  193. arr.push(t.evaluate(data)); // Evaluate the template with given data, and add the result HTML into temporary array
  194. }
  195. if(places.length === 0) { // Special case: no places availabe
  196. arr.push(this.templates.empty.evaluate({
  197. label: qype.str.no_data_available // Use text/HTML string defined in strings.js
  198. }));
  199. }
  200. return arr.join(""); // Return result array (HTML)
  201. }
  202. return "";
  203. },
  204. /**
  205. * Start retrieving the needed places via Qype API. Calls the API in
  206. * asynchronous manner.
  207. */
  208. getPlaces: function() {
  209. var loc = this.engine.getCurrentLocation();
  210. var params = {};
  211. if(this.currentCategoryId) {
  212. params.in_category = this.currentCategoryId;
  213. }
  214. /* *******************************
  215. PLACE OF CODE SNIPPET 12.3.2
  216. ********************************/
  217. qype.API.get("positions", {
  218. "onSuccess": this.onGetPlaces.bind(this),
  219. "onFailure": this.onFailure
  220. },
  221. loc.coords.latitude + "," + loc.coords.longitude + "/places",
  222. params
  223. );
  224. }
  225. /* *******************************
  226. PLACE OF CODE SNIPPET 12.3.1
  227. ********************************/
  228. ,
  229. /**
  230. * Gets called if the places were successfully retrieved from the Qype API.
  231. *
  232. * @param {Ajax.Response} response Response object as coming from the Prototype.js.
  233. */
  234. onGetPlaces: function(response) {
  235. util.log("{PlacesView.onGetPlaces} start");
  236. this.snippets.content.data.places = response.responseJSON;
  237. // Handle snippet/DOM updating manually.
  238. util.depressurizeAll("item");
  239. this.snippets.header.rewrite();
  240. this.snippets.content.rewrite();
  241. util.pressurizeAll("item");
  242. },
  243. onFailure: function(response) {
  244. util.log("{PlacesView.onFailure}: start, response = ");
  245. util.log(response);
  246. }
  247. });