1 | define([ |
---|
2 | "dojo/_base/kernel", |
---|
3 | "dojo/_base/array", |
---|
4 | "dojo/_base/connect", |
---|
5 | "dojo/_base/declare", |
---|
6 | "dojo/_base/event", |
---|
7 | "dojo/_base/lang", |
---|
8 | "dojo/_base/sniff", |
---|
9 | "dojo/dom-class", |
---|
10 | "dojo/dom-construct", |
---|
11 | "dojo/dom-style", |
---|
12 | "dijit/_Contained", |
---|
13 | "dijit/_Container", |
---|
14 | "dijit/_WidgetBase", |
---|
15 | "./PageIndicator", |
---|
16 | "./SwapView", |
---|
17 | "require" |
---|
18 | ], function(kernel, array, connect, declare, event, lang, has, domClass, domConstruct, domStyle, Contained, Container, WidgetBase, PageIndicator, SwapView, require){ |
---|
19 | |
---|
20 | /*===== |
---|
21 | var Contained = dijit._Contained; |
---|
22 | var Container = dijit._Container; |
---|
23 | var WidgetBase = dijit._WidgetBase; |
---|
24 | var PageIndicator = dojox.mobile.PageIndicator; |
---|
25 | var SwapView = dojox.mobile.SwapView; |
---|
26 | =====*/ |
---|
27 | |
---|
28 | // module: |
---|
29 | // dojox/mobile/Carousel |
---|
30 | // summary: |
---|
31 | // A carousel widget that manages a list of images |
---|
32 | |
---|
33 | kernel.experimental("dojox.mobile.Carousel"); |
---|
34 | |
---|
35 | return declare("dojox.mobile.Carousel", [WidgetBase, Container, Contained], { |
---|
36 | // summary: |
---|
37 | // A carousel widget that manages a list of images |
---|
38 | // description: |
---|
39 | // The carousel widget manages a list of images that can be |
---|
40 | // displayed horizontally, and allows the user to scroll through |
---|
41 | // the list and select a single item. |
---|
42 | |
---|
43 | // numVisible: Number |
---|
44 | // The number of visible items. |
---|
45 | numVisible: 3, |
---|
46 | |
---|
47 | // title: String |
---|
48 | // A title of the carousel to be displayed on the title bar. |
---|
49 | title: "", |
---|
50 | |
---|
51 | // pageIndicator: Boolean |
---|
52 | // If true, a page indicator, a series of small dots that indicate |
---|
53 | // the current page, is displayed on the title bar. |
---|
54 | pageIndicator: true, |
---|
55 | |
---|
56 | // navButton: Boolean |
---|
57 | // If true, navigation buttons are displyaed on the title bar. |
---|
58 | navButton: false, |
---|
59 | |
---|
60 | // height: String |
---|
61 | // Explicitly specified height of the widget (ex. "300px"). If |
---|
62 | // "inherit" is specified, the height is inherited from its offset |
---|
63 | // parent. |
---|
64 | height: "300px", |
---|
65 | |
---|
66 | // store: Object |
---|
67 | // Reference to data provider object used by this widget. |
---|
68 | store: null, |
---|
69 | |
---|
70 | // query: Object |
---|
71 | // A query that can be passed to 'store' to initially filter the |
---|
72 | // items. |
---|
73 | query: null, |
---|
74 | |
---|
75 | // queryOptions: Object |
---|
76 | // An optional parameter for the query. |
---|
77 | queryOptions: null, |
---|
78 | |
---|
79 | buildRendering: function(){ |
---|
80 | this.inherited(arguments); |
---|
81 | this.domNode.className = "mblCarousel"; |
---|
82 | var h; |
---|
83 | if(this.height === "inherit"){ |
---|
84 | if(this.domNode.offsetParent){ |
---|
85 | h = this.domNode.offsetParent.offsetHeight + "px"; |
---|
86 | } |
---|
87 | }else if(this.height){ |
---|
88 | h = this.height; |
---|
89 | } |
---|
90 | this.domNode.style.height = h; |
---|
91 | this.headerNode = domConstruct.create("DIV", {className:"mblCarouselHeaderBar"}, this.domNode); |
---|
92 | |
---|
93 | if(this.navButton){ |
---|
94 | this.btnContainerNode = domConstruct.create("DIV", { |
---|
95 | className: "mblCarouselBtnContainer" |
---|
96 | }, this.headerNode); |
---|
97 | domStyle.set(this.btnContainerNode, "float", "right"); // workaround for webkit rendering problem |
---|
98 | this.prevBtnNode = domConstruct.create("BUTTON", { |
---|
99 | className: "mblCarouselBtn", |
---|
100 | title: "Previous", |
---|
101 | innerHTML: "<" |
---|
102 | }, this.btnContainerNode); |
---|
103 | this.nextBtnNode = domConstruct.create("BUTTON", { |
---|
104 | className: "mblCarouselBtn", |
---|
105 | title: "Next", |
---|
106 | innerHTML: ">" |
---|
107 | }, this.btnContainerNode); |
---|
108 | this.connect(this.prevBtnNode, "onclick", "onPrevBtnClick"); |
---|
109 | this.connect(this.nextBtnNode, "onclick", "onNextBtnClick"); |
---|
110 | } |
---|
111 | |
---|
112 | if(this.pageIndicator){ |
---|
113 | if(!this.title){ |
---|
114 | this.title = " "; |
---|
115 | } |
---|
116 | this.piw = new PageIndicator(); |
---|
117 | domStyle.set(this.piw, "float", "right"); // workaround for webkit rendering problem |
---|
118 | this.headerNode.appendChild(this.piw.domNode); |
---|
119 | } |
---|
120 | |
---|
121 | this.titleNode = domConstruct.create("DIV", { |
---|
122 | className: "mblCarouselTitle" |
---|
123 | }, this.headerNode); |
---|
124 | |
---|
125 | this.containerNode = domConstruct.create("DIV", {className:"mblCarouselPages"}, this.domNode); |
---|
126 | connect.subscribe("/dojox/mobile/viewChanged", this, "handleViewChanged"); |
---|
127 | }, |
---|
128 | |
---|
129 | startup: function(){ |
---|
130 | if(this._started){ return; } |
---|
131 | if(this.store){ |
---|
132 | var store = this.store; |
---|
133 | this.store = null; |
---|
134 | this.setStore(store, this.query, this.queryOptions); |
---|
135 | } |
---|
136 | this.inherited(arguments); |
---|
137 | }, |
---|
138 | |
---|
139 | setStore: function(store, query, queryOptions){ |
---|
140 | // summary: |
---|
141 | // Sets the store to use with this widget. |
---|
142 | if(store === this.store){ return; } |
---|
143 | this.store = store; |
---|
144 | this.query = query; |
---|
145 | this.queryOptions = queryOptions; |
---|
146 | this.refresh(); |
---|
147 | }, |
---|
148 | |
---|
149 | refresh: function(){ |
---|
150 | if(!this.store){ return; } |
---|
151 | this.store.fetch({ |
---|
152 | query: this.query, |
---|
153 | queryOptions: this.queryOptions, |
---|
154 | onComplete: lang.hitch(this, "generate"), |
---|
155 | onError: lang.hitch(this, "onError") |
---|
156 | }); |
---|
157 | }, |
---|
158 | |
---|
159 | generate: function(/*Array*/items, /*Object*/ dataObject){ |
---|
160 | array.forEach(this.getChildren(), function(child){ |
---|
161 | if(child instanceof SwapView){ |
---|
162 | child.destroyRecursive(); |
---|
163 | } |
---|
164 | }); |
---|
165 | this.items = items; |
---|
166 | this.swapViews = []; |
---|
167 | this.images = []; |
---|
168 | var nPages = Math.ceil(items.length / this.numVisible); |
---|
169 | var h = this.domNode.offsetHeight - this.headerNode.offsetHeight; |
---|
170 | for(var i = 0; i < nPages; i++){ |
---|
171 | var w = new SwapView({height:h+"px"}); |
---|
172 | this.addChild(w); |
---|
173 | this.swapViews.push(w); |
---|
174 | w._carouselImages = []; |
---|
175 | if(i === 0 && this.piw){ |
---|
176 | this.piw.refId = w.id; |
---|
177 | } |
---|
178 | for(var j = 0; j < this.numVisible; j++){ |
---|
179 | var idx = i * this.numVisible + j; |
---|
180 | var item = idx < items.length ? items[idx] : |
---|
181 | {src:require.toUrl("dojo/resources/blank.gif"), height:"1px"}; |
---|
182 | var disp = w.domNode.style.display; |
---|
183 | w.domNode.style.display = ""; // need to be visible during the size calculation |
---|
184 | var box = this.createBox(item, h); |
---|
185 | w.containerNode.appendChild(box); |
---|
186 | box.appendChild(this.createHeaderText(item)); |
---|
187 | var img = this.createContent(item, idx); |
---|
188 | box.appendChild(img); |
---|
189 | box.appendChild(this.createFooterText(item)); |
---|
190 | this.resizeContent(item, box, img); |
---|
191 | w.domNode.style.display = disp; |
---|
192 | |
---|
193 | if(item.height !== "1px"){ |
---|
194 | this.images.push(img); |
---|
195 | w._carouselImages.push(img); |
---|
196 | } |
---|
197 | } |
---|
198 | } |
---|
199 | if(this.swapViews[0]){ |
---|
200 | this.loadImages(this.swapViews[0]); |
---|
201 | } |
---|
202 | if(this.swapViews[1]){ |
---|
203 | this.loadImages(this.swapViews[1]); // pre-fetch the next view images |
---|
204 | } |
---|
205 | this.currentView = this.swapViews[0]; |
---|
206 | if(this.piw){ |
---|
207 | this.piw.reset(); |
---|
208 | } |
---|
209 | }, |
---|
210 | |
---|
211 | createBox: function(item, h){ |
---|
212 | var width = item.width || (90/this.numVisible + "%"); |
---|
213 | var height = item.height || h + "px"; |
---|
214 | var m = has("ie") ? 5/this.numVisible-1 : 5/this.numVisible; |
---|
215 | var margin = item.margin || (m + "%"); |
---|
216 | var box = domConstruct.create("DIV", { |
---|
217 | className: "mblCarouselBox" |
---|
218 | }); |
---|
219 | domStyle.set(box, { |
---|
220 | margin: "0px " + margin, |
---|
221 | width: width, |
---|
222 | height: height |
---|
223 | }); |
---|
224 | return box; |
---|
225 | }, |
---|
226 | |
---|
227 | createHeaderText: function(item){ |
---|
228 | this.headerTextNode = domConstruct.create("DIV", { |
---|
229 | className: "mblCarouselImgHeaderText", |
---|
230 | innerHTML: item.headerText ? item.headerText : " " |
---|
231 | }); |
---|
232 | return this.headerTextNode; |
---|
233 | }, |
---|
234 | |
---|
235 | createContent: function(item, idx){ |
---|
236 | var props = { |
---|
237 | alt: item.alt || "", |
---|
238 | tabIndex: "0", // for keyboard navigation on a desktop browser |
---|
239 | className: "mblCarouselImg" |
---|
240 | }; |
---|
241 | var img = domConstruct.create("IMG", props); |
---|
242 | img._idx = idx; |
---|
243 | if(item.height !== "1px"){ |
---|
244 | this.connect(img, "onclick", "onClick"); |
---|
245 | this.connect(img, "onkeydown", "onClick"); |
---|
246 | connect.connect(img, "ondragstart", event.stop); |
---|
247 | }else{ |
---|
248 | img.style.visibility = "hidden"; |
---|
249 | } |
---|
250 | return img; |
---|
251 | }, |
---|
252 | |
---|
253 | createFooterText: function(item){ |
---|
254 | this.footerTextNode = domConstruct.create("DIV", { |
---|
255 | className: "mblCarouselImgFooterText", |
---|
256 | innerHTML: item.footerText ? item.footerText : " " |
---|
257 | }); |
---|
258 | return this.footerTextNode; |
---|
259 | }, |
---|
260 | |
---|
261 | resizeContent: function(item, box, img){ |
---|
262 | if(item.height !== "1px"){ |
---|
263 | img.style.height = (box.offsetHeight - this.headerTextNode.offsetHeight - this.footerTextNode.offsetHeight) + "px"; |
---|
264 | } |
---|
265 | }, |
---|
266 | |
---|
267 | onError: function(errText){ |
---|
268 | }, |
---|
269 | |
---|
270 | onPrevBtnClick: function(e){ |
---|
271 | if(this.currentView){ |
---|
272 | this.currentView.goTo(-1); |
---|
273 | } |
---|
274 | }, |
---|
275 | |
---|
276 | onNextBtnClick: function(e){ |
---|
277 | if(this.currentView){ |
---|
278 | this.currentView.goTo(1); |
---|
279 | } |
---|
280 | }, |
---|
281 | |
---|
282 | onClick: function(e){ |
---|
283 | if(e && e.type === "keydown" && e.keyCode !== 13){ return; } |
---|
284 | var img = e.currentTarget; |
---|
285 | for(var i = 0; i < this.images.length; i++){ |
---|
286 | if(this.images[i] === img){ |
---|
287 | domClass.add(img, "mblCarouselImgSelected"); |
---|
288 | }else{ |
---|
289 | domClass.remove(this.images[i], "mblCarouselImgSelected"); |
---|
290 | } |
---|
291 | } |
---|
292 | domStyle.set(img, "opacity", 0.4); |
---|
293 | setTimeout(function(){ |
---|
294 | domStyle.set(img, "opacity", 1); |
---|
295 | }, 1000); |
---|
296 | connect.publish("/dojox/mobile/carouselSelect", [this, img, this.items[img._idx], img._idx]); |
---|
297 | }, |
---|
298 | |
---|
299 | loadImages: function(view){ |
---|
300 | if(!view){ return; } |
---|
301 | var imgs = view._carouselImages; |
---|
302 | array.forEach(imgs, function(img){ |
---|
303 | if(!img.src){ |
---|
304 | var item = this.items[img._idx]; |
---|
305 | img.src = item.src; |
---|
306 | } |
---|
307 | }, this); |
---|
308 | }, |
---|
309 | |
---|
310 | handleViewChanged: function(view){ |
---|
311 | if(view.getParent() !== this){ return; } |
---|
312 | this.currentView = view; |
---|
313 | // lazy-load images in the next view |
---|
314 | this.loadImages(view.nextView(view.domNode)); |
---|
315 | }, |
---|
316 | |
---|
317 | _setTitleAttr: function(/*String*/title){ |
---|
318 | this.title = title; |
---|
319 | this.titleNode.innerHTML = this._cv ? this._cv(title) : title; |
---|
320 | } |
---|
321 | }); |
---|
322 | }); |
---|