1 /*jshint indent:2, curly:true, eqeqeq:true, immed:true, latedef:true,
2 newcap:true, noarg:true, regexp:true, undef:true, trailing:true,
4 /*global enyo:true, XT:true, _:true, document:true, window:true, XM:true */
9 var LOADING_SESSION = 1;
10 var LOADING_EXTENSIONS = 3;
11 var LOADING_SCHEMA = 4;
12 var LOADING_APP_DATA = 5;
16 Application kind. Contains two components, "postbooks", which is a module container,
20 /** @lends XV.App# */{
23 classes: "xt-app enyo-unselectable",
27 keyCapturePatterns: []
30 onListAdded: "addPulloutItem",
31 onModelChange: "modelChanged",
32 onParameterChange: "parameterDidChange",
33 onNavigatorEvent: "togglePullout",
34 onHistoryChange: "refreshHistoryPanel",
35 onHistoryItemSelected: "selectHistoryItem",
36 onSearch: "waterfallSearch",
37 onWorkspace: "waterfallWorkspace",
38 onColumnsChange: "columnsDidChange",
42 hotkey mode (triggered by alt)
43 notify popup mode (if the notify popup is showing)
46 handleKeyDown: function (inSender, inEvent) {
49 // remember the last 10 key presses
50 this._keyBufferArray.push(inEvent.keyCode);
51 this._keyBufferArray = this._keyBufferArray.slice(-10);
54 if (this.$.postbooks.isNotifyPopupShowing()) {
55 this.$.postbooks.notifyKey(inEvent.keyCode, inEvent.shiftKey);
60 inEvent.cancelBubble = true;
61 inEvent.returnValue = false;
62 this.processHotKey(inEvent.keyCode);
65 if (this._keyBufferEndPattern) {
66 // we're in record mode, so record.
67 this._keyBuffer = this._keyBuffer + String.fromCharCode(inEvent.keyCode);
70 if (this._keyBufferEndPattern &&
71 _.isEqual(this._keyBufferArray.slice(-1 * this._keyBufferEndPattern.length), this._keyBufferEndPattern) &&
72 this._falsePositives) {
73 this._falsePositives--;
75 } else if (this._keyBufferEndPattern &&
76 _.isEqual(this._keyBufferArray.slice(-1 * this._keyBufferEndPattern.length), this._keyBufferEndPattern)) {
77 // we've matched an end pattern. Send the recorded buffer to the appropriate method
78 this[this._keyBufferMethod](this._keyBuffer);
80 this._keyBufferEndPattern = undefined;
81 this._falsePositives = undefined;
84 // specification of the key capture patterns themselves are up to the subkind
85 _.each(this.getKeyCapturePatterns(), function (pattern) {
86 if (_.isEqual(that._keyBufferArray.slice(-1 * pattern.start.length), pattern.start)) {
87 // we've matched a start pattern. Now we're in record mode, waiting for a match to the end pattern
88 that._keyBuffer = that._keyBuffer || "";
89 that._keyBufferEndPattern = pattern.end;
90 that._keyBufferMethod = pattern.method;
91 that._falsePositives = pattern.falsePositives;
96 // the components array is overriden by the subkind
98 {name: "postbooks", kind: "XV.ModuleContainer", onTransitionStart: "handlePullout"},
99 {name: "pullout", kind: "XV.Pullout", onAnimateFinish: "pulloutAnimateFinish"},
100 {name: "signals", kind: "enyo.Signals", onkeydown: "handleKeyDown"},
102 state: UNINITIALIZED,
104 Passes the pullout payload straight from the sender (presumably the list
105 containing the pullout parameter) to the pullout, who will deal with
108 addPulloutItem: function (inSender, inEvent) {
109 if (!this.$.pullout) {
110 this._cachePullouts.push(inEvent);
113 this.$.pullout.addPulloutItem(inSender, inEvent);
115 columnsDidChange: function (inSender, inEvent) {
116 this.$.postbooks.getNavigator().waterfall("onColumnsChange", inEvent);
118 create: function () {
119 this._cachePullouts = [];
120 this._keyBufferArray = [];
121 this.inherited(arguments);
123 // make even modal popups hear keydown (necessary for keyboarding the notify popup)
124 enyo.dispatcher.autoForwardEvents.keydown = 1;
125 window.onbeforeunload = function () {
126 return "_exitPageWarning".loc();
129 handlePullout: function (inSender, inEvent) {
130 var showing = inSender.getActive().showPullout || false;
131 this.$.pullout.setShowing(showing);
134 When a model is changed, we want to reflect the new state across
135 the app, so we bubble all the way up there and waterfall down.
136 Currently, the only things that are updated by this process are the lists.
138 modelChanged: function (inSender, inEvent) {
140 // Waterfall down to all lists to tell them to update themselves
142 this.$.postbooks.getNavigator().waterfall("onModelChange", inEvent);
144 parameterDidChange: function (inSender, inEvent) {
145 if (this.getIsStarted()) {
146 this.$.postbooks.getNavigator().waterfall("onParameterChange", inEvent);
150 Implemented by the subkind
152 processHotKey: function (keyCode) {
155 * Manages the "lit-up-ness" of the icon buttons based on the pullout.
156 * If the pull-out is put away, we want all buttons to dim. If the pull-out
157 * is activated, we want the button related to the active pullout pane
158 * to light up. The presentation of these buttons take care of themselves
159 * if the user actually clicks on the buttons.
161 pulloutAnimateFinish: function (inSender, inEvent) {
162 var activeIconButton;
164 if (inSender.value === inSender.max) {
166 if (this.$.pullout.getSelectedPanel() === 'history') {
167 activeIconButton = 'history';
169 activeIconButton = 'search';
171 } else if (inSender.value === inSender.min) {
172 // pullout is inactive
173 activeIconButton = null;
175 this.$.postbooks.getNavigator().setActiveIconButton(activeIconButton);
177 refreshHistoryPanel: function (inSender, inEvent) {
178 this.$.pullout.refreshHistoryList();
181 When a history item is selected we bubble an event way up the application.
182 Note that we create a sort of ersatz model to mimic the way the handler
183 expects to have a model with the event to know what to drill down into.
185 selectHistoryItem: function (inSender, inEvent) {
186 inEvent.eventName = "onWorkspace";
187 this.waterfall("onWorkspace", inEvent);
189 startupProcess: function () {
190 var startupManager = XT.getStartupManager(),
191 progressBar = XT.app.$.postbooks.getStartupProgressBar(),
206 extensionPrivilegeName,
208 extensionsDownloaded = 0,
209 extensionPayloads = [],
212 eachCallback = function () {
213 var completed = startupManager.get('completed').length;
214 progressBar.animateProgressTo(completed);
215 if (completed === startupTaskCount) {
216 that.startupProcess();
220 // 1: Load session data
221 if (this.state === UNINITIALIZED) {
222 this.state = LOADING_SESSION;
223 startupManager.registerCallback(eachCallback, true);
224 XT.dataSource.connect();
225 startupTaskCount = startupManager.get('queue').length + startupManager.get('completed').length;
226 progressBar.setMax(startupTaskCount);
228 // #2 is off high-wire walking the Grand Canyon
230 // 3: Initialize extensions
231 } else if (this.state === LOADING_SESSION) {
233 this.state = LOADING_EXTENSIONS;
234 text = "_loadingExtensions".loc() + "...";
235 XT.app.$.postbooks.getStartupText().setContent(text);
236 for (prop in XT.extensions) {
237 if (XT.extensions.hasOwnProperty(prop)) {
238 ext = XT.extensions[prop];
239 for (extprop in ext) {
240 if (ext.hasOwnProperty(extprop) &&
241 typeof ext[extprop] === "function") {
242 //XT.log('Installing ' + prop + ' ' + extprop);
249 this.startupProcess();
252 } else if (this.state === LOADING_EXTENSIONS) {
253 this.state = LOADING_SCHEMA;
254 text = "_loadingSchema".loc() + "...";
255 XT.app.$.postbooks.getStartupText().setContent(text);
256 XT.StartupTask.create({
257 taskName: "loadSessionSchema",
261 success: function () {
263 that.startupProcess();
266 XT.session.loadSessionObjects(XT.session.SCHEMA, options);
270 // 5 Load Application Data
271 } else if (this.state === LOADING_SCHEMA) {
273 this.state = LOADING_APP_DATA;
274 text = "_loadingApplicationData".loc() + "...";
275 XT.app.$.postbooks.getStartupText().setContent(text);
276 progressBar.setMax(XT.StartupTasks.length);
277 progressBar.setProgress(0);
279 // there's a new startup task count now that
280 // the second stage of tasks are being loaded.
281 startupTaskCount = startupManager.get('queue').length +
282 startupManager.get('completed').length +
283 XT.StartupTasks.length;
285 // create a new each callback to manage the completion of this step
286 // the previously registered callback isn't doing any harm. It would be
287 // best to unregister the previous eachCallback and replicate the code
288 // here that advances the progress bar. This would make the animation look
289 // better. There's not a great way to unregister a startup callback, though.
290 eachCallback = function () {
291 var completed = startupManager.get('completed').length;
292 if (completed === startupTaskCount) {
293 that.startupProcess();
297 startupManager.registerCallback(eachCallback, true);
298 for (i = 0; i < XT.StartupTasks.length; i++) {
299 task = XT.StartupTasks[i];
300 XT.StartupTask.create(task);
304 } else if (this.state === LOADING_APP_DATA) {
305 // Go to the navigator
306 for (i = 0; i < this._cachePullouts.length; i++) {
307 inEvent = this._cachePullouts[i];
308 this.$.pullout.addPulloutItem(null, inEvent);
310 loginInfo = XT.app.$.postbooks.getNavigator().loginInfo();
311 details = XT.session.details;
312 loginInfo.setContent(details.username + " ยท " + details.organization);
313 this.state = RUNNING;
314 XT.app.$.postbooks.activate();
317 start: function (debug) {
318 if (this.getIsStarted()) { return; }
320 this.setDebug(debug);
322 // Run through the multi-step start process
323 this.startupProcess();
325 // lets not allow this to happen again
326 this.setIsStarted(true);
329 if (this.getShowing() && this.getIsStarted()) {
330 this.renderInto(document.body);
332 this.inherited(arguments);
335 togglePullout: function (inSender, inEvent) {
336 this.$.pullout.togglePullout(inSender, inEvent);
338 waterfallSearch: function (inSender, inEvent) {
339 this.$.postbooks.waterfall("onSearch", inEvent);
340 return true; // don't want to double up
342 waterfallWorkspace: function (inSender, inEvent) {
343 this.$.postbooks.waterfall("onWorkspace", inEvent);
344 return true; // don't want to double up