| Index: chrome/test/data/extensions/api_test/notifications/galore/app/controller.js
|
| diff --git a/chrome/test/data/extensions/api_test/notifications/galore/app/controller.js b/chrome/test/data/extensions/api_test/notifications/galore/app/controller.js
|
| index 5f5aa1ad6f3f19f6a4124318cc0dc4f00440576e..106fb24cdaf81b253d1bf5df8ec381269fca8f1e 100644
|
| --- a/chrome/test/data/extensions/api_test/notifications/galore/app/controller.js
|
| +++ b/chrome/test/data/extensions/api_test/notifications/galore/app/controller.js
|
| @@ -2,242 +2,359 @@
|
| // Use of this source code is governed by a BSD-style license that can be
|
| // found in the LICENSE file.
|
|
|
| -var Galore = Galore || {};
|
| -
|
| -Galore.controller = {
|
| - /** @constructor */
|
| - create: function() {
|
| - var controller = Object.create(this);
|
| - controller.api = chrome;
|
| - controller.counter = 0;
|
| - return controller;
|
| - },
|
| -
|
| - createWindow: function() {
|
| - chrome.storage.sync.get('settings', this.onSettingsFetched_.bind(this));
|
| - },
|
| -
|
| - /** @private */
|
| - onSettingsFetched_: function(items) {
|
| - var request = new XMLHttpRequest();
|
| - var settings = items.settings || {};
|
| - var source = settings.data || '/data/' + this.getDataVersion_();
|
| - request.open('GET', source, true);
|
| - request.responseType = 'text';
|
| - request.onload = this.onDataFetched_.bind(this, settings, request);
|
| - request.send();
|
| - },
|
| -
|
| - /** @private */
|
| - onDataFetched_: function(settings, request) {
|
| - var count = 0;
|
| - var data = JSON.parse(request.response);
|
| +var STOPPED = "Stopped";
|
| +var RECORDING = "Recording";
|
| +var PAUSED_RECORDING = "Recording Paused";
|
| +var PAUSED_PLAYING = "Playing Paused";
|
| +var PLAYING = "Playing";
|
| +
|
| +var recordingState = STOPPED;
|
| +
|
| +// Timestamp when current segment started.
|
| +var segmentStart;
|
| +// Segment duration accumulated before pause button was hit.
|
| +var pausedDuration;
|
| +// The array of segments, with delay and action.
|
| +var recordingList;
|
| +// When this timer fires, the next segment from recordingList should be played.
|
| +var playingTimer;
|
| +var currentSegmentIndex;
|
| +// A set of web Notifications - used to delete them during playback by id.
|
| +var webNotifications = {};
|
| +
|
| +var recorderButtons = [ "play", "record", "pause", "stop"];
|
| +var recorderButtonStates = [
|
| + { state: STOPPED, enabled: "play record" },
|
| + { state: RECORDING, enabled: "pause stop" },
|
| + { state: PAUSED_RECORDING, enabled: "record stop" },
|
| + { state: PAUSED_PLAYING, enabled: "play stop" },
|
| + { state: PLAYING, enabled: "pause stop" }
|
| +];
|
| +
|
| +// This function forms 2 selector lists - one that includes enabled buttons
|
| +// and one that includes disabled ones. Then it applies "disabled" attribute to
|
| +// corresponding sets of buttons.
|
| +function updateButtonsState() {
|
| + recorderButtonStates.map(function(entry) {
|
| + if (entry.state != recordingState)
|
| + return;
|
| + // Found entry with current recorder state. Now compute the sets
|
| + // of enabled/disabled buttons.
|
| + // Copy a list of all buttons.
|
| + var disabled = recorderButtons.slice(0);
|
| + // Get an array of enabled buttons for the state.
|
| + var enabled = entry.enabled.split(" ");
|
| + // Remove enabled buttons from disabled list, prefix them with "#" so they
|
| + // form proper id selectors.
|
| + for (var i = 0; i < enabled.length; i++) {
|
| + disabled.splice(disabled.indexOf(enabled[i]), 1);
|
| + enabled[i] = "#" + enabled[i];
|
| + }
|
| + // Prefix remaining disabled ids to form proper id selectors.
|
| + for (var i = 0; i < disabled.length; i++) {
|
| + disabled[i] = "#" + disabled[i];
|
| + }
|
| + getElements(disabled.join(", ")).forEach(function(element) {
|
| + element.setAttribute("disabled", "true")
|
| + })
|
| + getElements(enabled.join(", ")).forEach(function(element) {
|
| + element.removeAttribute("disabled")
|
| + })
|
| + })
|
| +}
|
| +
|
| +
|
| +function setRecordingState(newState) {
|
| + setRecorderStatusText(newState);
|
| + recordingState = newState;
|
| + updateButtonsState();
|
| +}
|
| +
|
| +function updateRecordingStats(context) {
|
| + var length = 0;
|
| + var segmentCnt = 0;
|
| + recordingList.slice(currentSegmentIndex).forEach(function(segment) {
|
| + length += segment.delay || 0;
|
| + segmentCnt++;
|
| + })
|
| + updateRecordingStatsDisplay(context + ": " + (segmentCnt-1) + " segments, " +
|
| + Math.floor(length/1000) + " seconds.");
|
| +}
|
| +
|
| +function loadRecording() {
|
| + chrome.storage.local.get("recording", function(items) {
|
| + recordingList = JSON.parse(items["recording"] || "[]");
|
| + setRecordingState(STOPPED);
|
| + updateRecordingStats("Loaded record");
|
| + })
|
| +}
|
| +
|
| +function finalizeRecording() {
|
| + chrome.storage.local.set({"recording": JSON.stringify(recordingList)});
|
| + updateRecordingStats("Recorded");
|
| +}
|
| +
|
| +function setPreviousSegmentDuration() {
|
| + var now = new Date().getTime();
|
| + var delay = now - segmentStart;
|
| + segmentStart = now;
|
| + recordingList[recordingList.length - 1].delay = delay;
|
| +}
|
| +
|
| +function recordCreate(kind, id, options) {
|
| + if (recordingState != RECORDING)
|
| + return;
|
| + setPreviousSegmentDuration();
|
| + recordingList.push({ type: "create", kind: kind, id: id, options: options });
|
| + updateRecordingStats("Recording");
|
| +}
|
| +
|
| +function recordDelete(kind, id) {
|
| + if (recordingState != RECORDING)
|
| + return;
|
| + setPreviousSegmentDuration();
|
| + recordingList.push({ type: "delete", kind: kind, id: id });
|
| + updateRecordingStats("Recording");
|
| +}
|
| +
|
| +function startPlaying() {
|
| + if (recordingList.length < 2)
|
| + return false;
|
| +
|
| + setRecordingState(PLAYING);
|
| +
|
| + if (playingTimer)
|
| + clearTimeout(playingTimer);
|
| +
|
| + webNotifications = {};
|
| + currentSegmentIndex = 0;
|
| + playingTimer = setTimeout(playNextSegment,
|
| + recordingList[currentSegmentIndex].delay);
|
| + updateRecordingStats("Playing");
|
| +}
|
| +
|
| +function playNextSegment() {
|
| + currentSegmentIndex++;
|
| + var segment = recordingList[currentSegmentIndex];
|
| + if (!segment) {
|
| + stopPlaying();
|
| + return;
|
| + }
|
| +
|
| + if (segment.type == "create") {
|
| + createNotificationForPlay(segment.kind, segment.id, segment.options);
|
| + } else { // type == "delete"
|
| + deleteNotificationForPlay(segment.kind, segment.id);
|
| + }
|
| + playingTimer = setTimeout(playNextSegment,
|
| + recordingList[currentSegmentIndex].delay);
|
| + segmentStart = new Date().getTime();
|
| + updateRecordingStats("Playing");
|
| +}
|
| +
|
| +function deleteNotificationForPlay(kind, id) {
|
| + if (kind == 'web') {
|
| + webNotifications[id].close();
|
| + } else {
|
| + chrome.notifications.clear(id, function(wasClosed) {
|
| + // nothing to do
|
| + });
|
| + }
|
| +}
|
| +
|
| +function createNotificationForPlay(kind, id, options) {
|
| + if (kind == 'web') {
|
| + webNotifications[id] = createWebNotification(id, options);
|
| + } else {
|
| + var type = options.type;
|
| + var priority = options.priority;
|
| + createRichNotification(id, type, priority, options);
|
| + }
|
| +}
|
| +function stopPlaying() {
|
| + currentSegmentIndex = 0;
|
| + clearTimeout(playingTimer);
|
| + updateRecordingStats("Record");
|
| + setRecordingState(STOPPED);
|
| +}
|
| +
|
| +function pausePlaying() {
|
| + clearTimeout(playingTimer);
|
| + pausedDuration = new Date().getTime() - segmentStart;
|
| + setRecordingState(PAUSED_PLAYING);
|
| +}
|
| +
|
| +function unpausePlaying() {
|
| + var remainingInSegment =
|
| + recordingList[currentSegmentIndex].delay - pausedDuration;
|
| + if (remainingInSegment < 0)
|
| + remainingInSegment = 0;
|
| + playingTimer = setTimeout(playNextSegment, remainingInSegment);
|
| + segmentStart = new Date().getTime() - pausedDuration;
|
| +}
|
| +
|
| +function onRecord() {
|
| + if (recordingState == STOPPED) {
|
| + segmentStart = new Date().getTime();
|
| + pausedDuration = 0;
|
| + // This item is only needed to keep a duration of the delay between start
|
| + // and first action.
|
| + recordingList = [ { type:"start" } ];
|
| + } else if (recordingState == PAUSED_RECORDING) {
|
| + segmentStart = new Date().getTime() - pausedDuration;
|
| + pausedDuration = 0;
|
| + } else {
|
| + return;
|
| + }
|
| + updateRecordingStats("Recording");
|
| + setRecordingState(RECORDING);
|
| +}
|
| +
|
| +function pauseRecording() {
|
| + pausedDuration = new Date().getTime() - segmentStart;
|
| + segmentStart = 0;
|
| + setRecordingState(PAUSED_RECORDING);
|
| +}
|
| +
|
| +function onPause() {
|
| + if (recordingState == RECORDING) {
|
| + pauseRecording();
|
| + } else if (recordingState == PLAYING) {
|
| + pausePlaying();
|
| + } else {
|
| + return;
|
| + }
|
| +}
|
| +
|
| +function onStop() {
|
| + switch (recordingState) {
|
| + case PAUSED_RECORDING:
|
| + segmentStart = new Date().getTime() - pausedDuration;
|
| + // fall through
|
| + case RECORDING:
|
| + finalizeRecording();
|
| + break;
|
| + case PLAYING:
|
| + case PAUSED_PLAYING:
|
| + stopPlaying();
|
| + break;
|
| + }
|
| + setRecordingState(STOPPED);
|
| +}
|
| +
|
| +function onPlay() {
|
| + if (recordingState == STOPPED) {
|
| + if (!startPlaying())
|
| + return;
|
| + } else if (recordingState == PAUSED_PLAYING) {
|
| + unpausePlaying();
|
| + }
|
| + setRecordingState(PLAYING);
|
| +}
|
| +
|
| +function createWindow() {
|
| + chrome.storage.local.get('settings', onSettingsFetched);
|
| +}
|
| +
|
| +function onSettingsFetched(items) {
|
| + settings = items.settings || settings;
|
| + var request = new XMLHttpRequest();
|
| + var source = '/data/data.json';
|
| + request.open('GET', source, true);
|
| + request.responseType = 'text';
|
| + request.onload = onDataFetched;
|
| + request.send();
|
| +}
|
| +
|
| +function onDataFetched() {
|
| + var data = JSON.parse(this.response);
|
| + createAppWindow(function() {
|
| + // Create notification buttons.
|
| data.forEach(function(section) {
|
| + var type = section.notificationType;
|
| (section.notificationOptions || []).forEach(function(options) {
|
| - ++count;
|
| - this.fetchImages_(options, function() {
|
| - if (--count == 0)
|
| - this.onImagesFetched_(settings, data);
|
| - }.bind(this));
|
| - }, this);
|
| - }, this);
|
| - },
|
| -
|
| - /** @private */
|
| - onImagesFetched_: function(settings, data) {
|
| - this.settings = settings;
|
| - this.view = Galore.view.create(this.settings, function() {
|
| - // Create buttons.
|
| - data.forEach(function(section) {
|
| - var defaults = section.globals || data[0].globals;
|
| - var type = section.notificationType;
|
| - (section.notificationOptions || []).forEach(function(options) {
|
| - var defaulted = this.getDefaultedOptions_(options, defaults);
|
| - var create = this.createNotification_.bind(this, type, defaulted);
|
| - this.view.addNotificationButton(section.sectionName,
|
| - defaulted.title,
|
| - defaulted.iconUrl,
|
| - create);
|
| - }, this);
|
| - }, this);
|
| - // Set the API entry point and use it to set event listeners.
|
| - this.api = this.getApi_(data);
|
| - if (this.api)
|
| - this.addListeners_(this.api, data[0].events);
|
| - // Display the completed and ready window.
|
| - this.view.showWindow();
|
| - }.bind(this), this.onSettingsChange_.bind(this));
|
| - },
|
| -
|
| - /** @private */
|
| - fetchImages_: function(options, onFetched) {
|
| - var count = 0;
|
| - var replacements = {};
|
| - this.mapStrings_(options, function(string) {
|
| - if (string.indexOf("/images/") == 0 || string.search(/https?:\//) == 0) {
|
| - ++count;
|
| - this.fetchImage_(string, function(url) {
|
| - replacements[string] = url;
|
| - if (--count == 0) {
|
| - this.mapStrings_(options, function(string) {
|
| - return replacements[string] || string;
|
| - });
|
| - onFetched.call(this, options);
|
| - }
|
| - });
|
| - }
|
| - });
|
| - },
|
| -
|
| - /** @private */
|
| - fetchImage_: function(url, onFetched) {
|
| - var request = new XMLHttpRequest();
|
| - request.open('GET', url, true);
|
| - request.responseType = 'blob';
|
| - request.onload = function() {
|
| - var url = window.URL.createObjectURL(request.response);
|
| - onFetched.call(this, url);
|
| - }.bind(this);
|
| - request.send();
|
| - },
|
| -
|
| - /** @private */
|
| - onSettingsChange_: function(settings) {
|
| - this.settings = settings;
|
| - chrome.storage.sync.set({settings: this.settings});
|
| - },
|
| -
|
| - /** @private */
|
| - createNotification_: function(type, options) {
|
| - var id = this.getNextId_();
|
| - var priority = Number(this.settings.priority || 0);
|
| - var expanded = this.getExpandedOptions_(options, id, type, priority);
|
| - if (type == 'webkit')
|
| - this.createWebKitNotification_(expanded);
|
| - else
|
| - this.createRichNotification_(expanded, id, type, priority);
|
| - },
|
| -
|
| - /** @private */
|
| - createWebKitNotification_: function(options) {
|
| - var iconUrl = options.iconUrl;
|
| - var title = options.title;
|
| - var message = options.message;
|
| - new Notification(title, {
|
| - body: message,
|
| - icon: iconUrl
|
| + addNotificationButton(section.sectionName,
|
| + options.title,
|
| + options.iconUrl,
|
| + function() { createNotification(type, options) });
|
| + });
|
| });
|
| - this.handleEvent_('create', '?', 'title: "' + title + '"');
|
| - },
|
| -
|
| - /** @private */
|
| - createRichNotification_: function(options, id, type, priority) {
|
| - this.api.create(id, options, function() {
|
| - var argument1 = 'type: "' + type + '"';
|
| - var argument2 = 'priority: ' + priority;
|
| - var argument3 = 'title: "' + options.title + '"';
|
| - this.handleEvent_('create', id, argument1, argument2, argument3);
|
| - }.bind(this));
|
| - },
|
| -
|
| - /** @private */
|
| - getNextId_: function() {
|
| - this.counter += 1;
|
| - return String(this.counter);
|
| - },
|
| -
|
| - /** @private */
|
| - getDefaultedOptions_: function(options, defaults) {
|
| - var defaulted = this.deepCopy_(options);
|
| - Object.keys(defaults || {}).forEach(function (key) {
|
| - defaulted[key] = options[key] || defaults[key];
|
| - });
|
| - return defaulted;
|
| - },
|
| -
|
| - /** @private */
|
| - getExpandedOptions_: function(options, id, type, priority) {
|
| - var expanded = this.deepCopy_(options);
|
| - return this.mapStrings_(expanded, function(string) {
|
| - return this.getExpandedOption_(string, id, type, priority);
|
| - }, this);
|
| - },
|
| -
|
| - /** @private */
|
| - getExpandedOption_: function(option, id, type, priority) {
|
| - if (option == '$!') {
|
| - option = priority; // Avoids making priorities into strings.
|
| - } else {
|
| - option = option.replace(/\$#/g, id);
|
| - option = option.replace(/\$\?/g, type);
|
| - option = option.replace(/\$\!/g, priority);
|
| - }
|
| - return option;
|
| - },
|
| -
|
| - /** @private */
|
| - deepCopy_: function(value) {
|
| - var copy = value;
|
| - if (Array.isArray(value)) {
|
| - copy = value.map(this.deepCopy_, this);
|
| - } else if (value && typeof value === 'object') {
|
| - copy = {}
|
| - Object.keys(value).forEach(function (key) {
|
| - copy[key] = this.deepCopy_(value[key]);
|
| - }, this);
|
| - }
|
| - return copy;
|
| - },
|
| -
|
| - /** @private */
|
| - mapStrings_: function(value, map) {
|
| - var mapped = value;
|
| - if (typeof value === 'string') {
|
| - mapped = map.call(this, value);
|
| - mapped = (typeof mapped !== 'undefined') ? mapped : value;
|
| - } else if (value && typeof value == 'object') {
|
| - Object.keys(value).forEach(function (key) {
|
| - mapped[key] = this.mapStrings_(value[key], map);
|
| - }, this);
|
| - }
|
| - return mapped;
|
| - },
|
| -
|
| - /** @private */
|
| - addListeners_: function(api, events) {
|
| - (events || []).forEach(function(event) {
|
| - var listener = this.handleEvent_.bind(this, event);
|
| - if (api[event])
|
| - api[event].addListener(listener);
|
| - else
|
| - console.log('Event ' + event + ' not defined.');
|
| - }, this);
|
| - },
|
| -
|
| - /** @private */
|
| - handleEvent_: function(event, id, var_args) {
|
| - this.view.logEvent('Notification #' + id + ': ' + event + '(' +
|
| - Array.prototype.slice.call(arguments, 2).join(', ') +
|
| - ')');
|
| - },
|
| -
|
| - /** @private */
|
| - getDataVersion_: function() {
|
| - var version = navigator.appVersion.replace(/^.* Chrome\//, '');
|
| - return (version > '28.0.1500.70') ? '28.0.1500.70.json' :
|
| - (version > '27.0.1433.1') ? '27.0.1433.1.json' :
|
| - (version > '27.0.1432.2') ? '27.0.1432.2.json' :
|
| - '27.0.0.0.json';
|
| - },
|
| -
|
| - /** @private */
|
| - getApi_: function(data) {
|
| - var path = data[0].api || 'notifications';
|
| - var api = chrome;
|
| - path.split('.').forEach(function(key) { api = api && api[key]; });
|
| - if (!api)
|
| - this.view.logError('No API found - chrome.' + path + ' is undefined');
|
| - return api;
|
| + loadRecording();
|
| + addListeners();
|
| + showWindow();
|
| + });
|
| +}
|
| +
|
| +function onSettingsChange(settings) {
|
| + chrome.storage.local.set({settings: settings});
|
| +}
|
| +
|
| +function createNotification(type, options) {
|
| +var id = getNextId();
|
| +var priority = Number(settings.priority || 0);
|
| +if (type == 'web')
|
| + createWebNotification(id, options);
|
| + else
|
| + createRichNotification(id, type, priority, options);
|
| +}
|
| +
|
| +function createWebNotification(id, options) {
|
| + var iconUrl = options.iconUrl;
|
| + var title = options.title;
|
| + var message = options.message;
|
| + var n = new Notification(title, {
|
| + body: message,
|
| + icon: iconUrl,
|
| + tag: id
|
| + });
|
| + n.onshow = function() { logEvent('WebNotification #' + id + ': onshow'); }
|
| + n.onclick = function() { logEvent('WebNotification #' + id + ': onclick'); }
|
| + n.onclose = function() {
|
| + logEvent('WebNotification #' + id + ': onclose');
|
| + recordDelete('web', id);
|
| }
|
| + logCreate('Web', id, 'title: "' + title + '"');
|
| + recordCreate('web', id, options);
|
| + return n;
|
| +}
|
| +
|
| +function createRichNotification(id, type, priority, options) {
|
| + options["type"] = type;
|
| + options["priority"] = priority;
|
| + chrome.notifications.create(id, options, function() {
|
| + var argument1 = 'type: "' + type + '"';
|
| + var argument2 = 'priority: ' + priority;
|
| + var argument3 = 'title: "' + options.title + '"';
|
| + logCreate('Rich', id, argument1, argument2, argument3);
|
| + });
|
| + recordCreate('rich', id, options);
|
| +}
|
| +
|
| +var counter = 0;
|
| +function getNextId() {
|
| + return String(counter++);
|
| +}
|
| +
|
| +function addListeners() {
|
| + chrome.notifications.onClosed.addListener(onClosed);
|
| + chrome.notifications.onClicked.addListener(onClicked);
|
| + chrome.notifications.onButtonClicked.addListener(onButtonClicked);
|
| +}
|
| +
|
| +function logCreate(kind, id, var_args) {
|
| + logEvent(kind + ' Notification #' + id + ': created ' + '(' +
|
| + Array.prototype.slice.call(arguments, 2).join(', ') + ')');
|
| +}
|
| +
|
| +function onClosed(id) {
|
| + logEvent('Notification #' + id + ': onClosed');
|
| + recordDelete('rich', id);
|
| +}
|
| +
|
| +function onClicked(id) {
|
| + logEvent('Notification #' + id + ': onClicked');
|
| +}
|
|
|
| -};
|
| +function onButtonClicked(id, index) {
|
| + logEvent('Notification #' + id + ': onButtonClicked, btn: ' + index);
|
| +}
|
|
|