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# */{
22 classes: "enyo-fit enyo-unselectable",
26 keyCapturePatterns: []
29 onListAdded: "addPulloutItem",
30 onModelChange: "modelChanged",
31 onParameterChange: "parameterDidChange",
32 onNavigatorEvent: "togglePullout",
33 onHistoryChange: "refreshHistoryPanel",
34 onHistoryItemSelected: "selectHistoryItem",
35 onSearch: "waterfallSearch",
36 onWorkspace: "waterfallWorkspace",
37 onColumnsChange: "columnsDidChange",
38 onWorkspaceAction: "waterfallWorkspaceAction"
42 hotkey mode (triggered by alt)
43 notify popup mode (if the notify popup is showing)
46 handleKeyDown: function (inSender, inEvent) {
48 keyCode = inEvent.keyCode;
50 // remember the last 10 key presses
51 this._keyBufferArray.push(keyCode);
52 this._keyBufferArray = this._keyBufferArray.slice(-10);
55 if (this.$.postbooks.isNotifyPopupShowing()) {
56 this.$.postbooks.notifyKey(keyCode, inEvent.shiftKey);
60 if (keyCode === 189) {
61 // XXX FIXME hack. Dashes aren't coming through as dashes from my keyboard
62 keyCode = '-'.charCodeAt(0);
65 inEvent.cancelBubble = true;
66 inEvent.returnValue = false;
67 this.processHotKey(keyCode);
70 if (this._keyBufferEndPattern) {
71 // we're in record mode, so record.
72 this._keyBuffer = this._keyBuffer + String.fromCharCode(keyCode);
75 if (this._keyBufferEndPattern &&
76 _.isEqual(this._keyBufferArray.slice(-1 * this._keyBufferEndPattern.length), this._keyBufferEndPattern) &&
77 this._falsePositives) {
78 this._falsePositives--;
80 } else if (this._keyBufferEndPattern &&
81 _.isEqual(this._keyBufferArray.slice(-1 * this._keyBufferEndPattern.length), this._keyBufferEndPattern)) {
83 // first slice the end pattern off the payload
84 this._keyBuffer = this._keyBuffer.substring(0, this._keyBuffer.length - this._keyBufferEndPattern.length);
86 // we've matched an end pattern. Send the recorded buffer to the appropriate method
87 this[this._keyBufferMethod](this._keyBuffer);
89 this._keyBufferEndPattern = undefined;
90 this._falsePositives = undefined;
93 // specification of the key capture patterns themselves are up to the subkind
94 _.each(this.getKeyCapturePatterns(), function (pattern) {
95 if (_.isEqual(that._keyBufferArray.slice(-1 * pattern.start.length), pattern.start)) {
96 // we've matched a start pattern. Now we're in record mode, waiting for a match to the end pattern
97 that._keyBuffer = that._keyBuffer || "";
98 that._keyBufferEndPattern = pattern.end;
99 that._keyBufferMethod = pattern.method;
100 that._falsePositives = pattern.falsePositives;
105 // the components array is overriden by the subkind
107 {name: "postbooks", kind: "XV.ModuleContainer", onTransitionStart: "handlePullout"},
108 {name: "pullout", kind: "XV.Pullout", onAnimateFinish: "pulloutAnimateFinish"},
109 {name: "signals", kind: "enyo.Signals", onkeydown: "handleKeyDown"},
111 state: UNINITIALIZED,
113 Passes the pullout payload straight from the sender (presumably the list
114 containing the pullout parameter) to the pullout, who will deal with
117 addPulloutItem: function (inSender, inEvent) {
118 if (!this.$.pullout) {
119 this._cachePullouts.push(inEvent);
122 this.$.pullout.addPulloutItem(inSender, inEvent);
124 columnsDidChange: function (inSender, inEvent) {
125 this.$.postbooks.getNavigator().waterfall("onColumnsChange", inEvent);
127 create: function () {
128 this._cachePullouts = [];
129 this._keyBufferArray = [];
130 this.inherited(arguments);
132 // make even modal popups hear keydown (necessary for keyboarding the notify popup)
133 enyo.dispatcher.autoForwardEvents.keydown = 1;
134 window.onbeforeunload = function () {
135 return "_exitPageWarning".loc();
138 handlePullout: function (inSender, inEvent) {
139 var showing = inSender.getActive().showPullout || false;
140 this.$.pullout.setShowing(showing);
143 When a model is changed, we want to reflect the new state across
144 the app, so we bubble all the way up there and waterfall down.
145 Currently, the only things that are updated by this process are the lists.
147 modelChanged: function (inSender, inEvent) {
149 // Waterfall down to all lists to tell them to update themselves
151 this.$.postbooks.getNavigator().waterfall("onModelChange", inEvent);
153 parameterDidChange: function (inSender, inEvent) {
154 if (this.getIsStarted()) {
155 this.$.postbooks.getNavigator().waterfall("onParameterChange", inEvent);
159 Implemented by the subkind
161 processHotKey: function (keyCode) {
164 * Manages the "lit-up-ness" of the icon buttons based on the pullout.
165 * If the pull-out is put away, we want all buttons to dim. If the pull-out
166 * is activated, we want the button related to the active pullout pane
167 * to light up. The presentation of these buttons take care of themselves
168 * if the user actually clicks on the buttons.
170 pulloutAnimateFinish: function (inSender, inEvent) {
171 var activeIconButton;
173 if (inSender.value === inSender.max) {
175 if (this.$.pullout.getSelectedPanel() === 'history') {
176 activeIconButton = 'history';
178 activeIconButton = 'search';
180 } else if (inSender.value === inSender.min) {
181 // pullout is inactive
182 activeIconButton = null;
184 this.$.postbooks.getNavigator().setActiveIconButton(activeIconButton);
186 refreshHistoryPanel: function (inSender, inEvent) {
187 this.$.pullout.refreshHistoryList();
190 When a history item is selected we bubble an event way up the application.
191 Note that we create a sort of ersatz model to mimic the way the handler
192 expects to have a model with the event to know what to drill down into.
194 selectHistoryItem: function (inSender, inEvent) {
195 inEvent.eventName = "onWorkspace";
196 this.waterfall("onWorkspace", inEvent);
198 startupProcess: function () {
199 var startupManager = XT.getStartupManager(),
200 progressBar = XT.app.$.postbooks.getStartupProgressBar(),
215 extensionPrivilegeName,
217 extensionsDownloaded = 0,
218 extensionPayloads = [],
221 eachCallback = function () {
222 var completed = startupManager.get('completed').length;
223 progressBar.animateProgressTo(completed);
224 if (completed === startupTaskCount) {
225 that.startupProcess();
229 // 1: Load session data
230 if (this.state === UNINITIALIZED) {
231 this.state = LOADING_SESSION;
232 startupManager.registerCallback(eachCallback, true);
233 XT.dataSource.connect();
234 startupTaskCount = startupManager.get('queue').length + startupManager.get('completed').length;
235 progressBar.setMax(startupTaskCount);
237 // #2 is off high-wire walking the Grand Canyon
239 // 3: Initialize extensions
240 } else if (this.state === LOADING_SESSION) {
242 this.state = LOADING_EXTENSIONS;
243 text = "_loadingExtensions".loc() + "...";
244 XT.app.$.postbooks.getStartupText().setContent(text);
245 for (prop in XT.extensions) {
246 if (XT.extensions.hasOwnProperty(prop)) {
247 ext = XT.extensions[prop];
248 for (extprop in ext) {
249 if (ext.hasOwnProperty(extprop) &&
250 typeof ext[extprop] === "function") {
251 //XT.log('Installing ' + prop + ' ' + extprop);
258 this.startupProcess();
261 } else if (this.state === LOADING_EXTENSIONS) {
262 this.state = LOADING_SCHEMA;
263 text = "_loadingSchema".loc() + "...";
264 XT.app.$.postbooks.getStartupText().setContent(text);
265 XT.StartupTask.create({
266 taskName: "loadSessionSchema",
270 success: function () {
272 that.startupProcess();
275 XT.session.loadSessionObjects(XT.session.SCHEMA, options);
279 // 5 Load Application Data
280 } else if (this.state === LOADING_SCHEMA) {
282 this.state = LOADING_APP_DATA;
283 text = "_loadingApplicationData".loc() + "...";
284 XT.app.$.postbooks.getStartupText().setContent(text);
285 progressBar.setMax(XT.StartupTasks.length);
286 progressBar.setProgress(0);
288 // there's a new startup task count now that
289 // the second stage of tasks are being loaded.
290 startupTaskCount = startupManager.get('queue').length +
291 startupManager.get('completed').length +
292 XT.StartupTasks.length;
294 // create a new each callback to manage the completion of this step
295 // the previously registered callback isn't doing any harm. It would be
296 // best to unregister the previous eachCallback and replicate the code
297 // here that advances the progress bar. This would make the animation look
298 // better. There's not a great way to unregister a startup callback, though.
299 eachCallback = function () {
300 var completed = startupManager.get('completed').length;
301 if (completed === startupTaskCount) {
302 that.startupProcess();
306 startupManager.registerCallback(eachCallback, true);
307 for (i = 0; i < XT.StartupTasks.length; i++) {
308 task = XT.StartupTasks[i];
309 XT.StartupTask.create(task);
313 } else if (this.state === LOADING_APP_DATA) {
314 // Go to the navigator
315 for (i = 0; i < this._cachePullouts.length; i++) {
316 inEvent = this._cachePullouts[i];
317 this.$.pullout.addPulloutItem(null, inEvent);
319 loginInfo = XT.app.$.postbooks.getNavigator().loginInfo();
320 details = XT.session.details;
321 loginInfo.setContent(details.username + " ยท " + details.organization);
322 this.state = RUNNING;
323 XT.app.$.postbooks.activate();
326 start: function (debug) {
327 if (this.getIsStarted()) { return; }
329 this.setDebug(debug);
331 // Run through the multi-step start process
332 this.startupProcess();
334 // lets not allow this to happen again
335 this.setIsStarted(true);
338 if (this.getShowing() && this.getIsStarted()) {
339 this.renderInto(document.body);
341 this.inherited(arguments);
344 togglePullout: function (inSender, inEvent) {
345 this.$.pullout.togglePullout(inSender, inEvent);
347 waterfallSearch: function (inSender, inEvent) {
348 this.$.postbooks.waterfall("onSearch", inEvent);
349 return true; // don't want to double up
351 waterfallWorkspace: function (inSender, inEvent) {
352 this.$.postbooks.waterfall("onWorkspace", inEvent);
353 return true; // don't want to double up
355 waterfallWorkspaceAction: function (inSender, inEvent) {
356 this.$.postbooks.waterfall("onWorkspaceAction", inEvent);
357 return true; // don't want to double up