Merge pull request #2229 from edx/anton/cookie-storage

Video: Add utility to work with cookies.
This commit is contained in:
Anton Stupak
2014-01-22 01:51:15 -08:00
7 changed files with 365 additions and 4 deletions

View File

@@ -0,0 +1,149 @@
(function (requirejs, require, define) {
require(
['video/00_cookie_storage.js'],
function (CookieStorage) {
describe('CookieStorage', function () {
var mostRecentCall;
beforeEach(function () {
mostRecentCall = $.cookie.mostRecentCall;
});
afterEach(function () {
CookieStorage('test_storage').clear();
});
describe('intialize', function () {
it('with namespace', function () {
var storage = CookieStorage('test_storage');
storage.setItem('item_1', 'value_1');
expect(mostRecentCall.args[0]).toBe('test_storage');
});
it('without namespace', function () {
var storage = CookieStorage();
storage.setItem('item_1', 'value_1');
expect(mostRecentCall.args[0]).toBe('cookieStorage');
});
});
it('unload', function () {
var expected = JSON.stringify({
storage: {
'item_2': {
value: 'value_2',
session: false
}
},
keys: ['item_2']
}),
storage = CookieStorage('test_storage');
storage.setItem('item_1', 'value_1', true);
storage.setItem('item_2', 'value_2');
$(window).trigger('unload');
expect(mostRecentCall.args[1]).toBe(expected);
});
describe('methods: ', function () {
var data = {
storage: {
'item_1': {
value: 'value_1',
session: false
}
},
keys: ['item_1']
},
storage;
beforeEach(function () {
$.cookie.andReturn(JSON.stringify(data));
storage = CookieStorage('test_storage');
});
describe('setItem', function () {
it('pass correct data', function () {
var expected = JSON.stringify({
storage: {
'item_1': {
value: 'value_1',
session: false
},
'item_2': {
value: 'value_2',
session: false
},
'item_3': {
value: 'value_3',
session: true
},
},
keys: ['item_1', 'item_2', 'item_3']
});
storage.setItem('item_2', 'value_2');
storage.setItem('item_3', 'value_3', true);
expect(mostRecentCall.args[0]).toBe('test_storage');
expect(mostRecentCall.args[1]).toBe(expected);
});
it('pass broken arguments', function () {
$.cookie.reset();
storage.setItem(null, 'value_1');
expect($.cookie).not.toHaveBeenCalled();
});
});
describe('getItem', function () {
it('item exist', function () {
$.each(data['storage'], function(key, value) {
expect(storage.getItem(key)).toBe(value['value']);
});
});
it('item does not exist', function () {
expect(storage.getItem('nonexistent')).toBe(null);
});
});
describe('removeItem', function () {
it('item exist', function () {
var expected = JSON.stringify({
storage: {},
keys: []
});
storage.removeItem('item_1');
expect(mostRecentCall.args[1]).toBe(expected);
});
it('item does not exist', function () {
storage.removeItem('nonexistent');
expect(mostRecentCall.args[1]).toBe(JSON.stringify(data));
});
});
it('clear', function () {
storage.clear();
expect(mostRecentCall.args[1]).toBe(null);
});
describe('key', function () {
it('key exist', function () {
$.each(data['keys'], function(index, name) {
expect(storage.key(index)).toBe(name);
});
});
it('key is grater than keys list', function () {
expect(storage.key(100)).toBe(null);
});
});
});
});
});
}(RequireJS.requirejs, RequireJS.require, RequireJS.define));

View File

@@ -22,11 +22,12 @@
describe('constructor', function() {
beforeEach(function() {
spyOn($.fn, 'slider').andCallThrough();
$.cookie.andReturn('75');
initialize();
});
it('initialize currentVolume to 100', function() {
expect(state.videoVolumeControl.currentVolume).toEqual(1);
it('initialize currentVolume to 75', function() {
expect(state.videoVolumeControl.currentVolume).toEqual(75);
});
it('render the volume control', function() {

View File

@@ -0,0 +1,196 @@
(function (requirejs, require, define) {
define(
'video/00_cookie_storage.js',
[],
function() {
"use strict";
/**
* Provides convenient way to work with cookies.
*
* Maximum 4096 bytes can be stored per namespace.
*
* @TODO: Uses localStorage if available.
*
* @param {string} namespace Namespace that is used to store data.
* @return {object} CookieStorage API.
*/
var CookieStorage = function (namespace) {
var Storage;
/**
* Returns an empty storage with proper data structure.
*
* @private
* @return {object} Empty storage.
*/
var _getEmptyStorage = function () {
return {
storage: {},
keys: []
};
};
/**
* Returns the current value associated with the given namespace.
* If data doesn't exist or has data with incorrect interface, it creates
* an empty storage with proper data structure.
*
* @private
* @param {string} namespace Namespace that is used to store data.
* @return {object} Stored data or an empty storage.
*/
var _getData = function (namespace) {
var data;
try {
data = JSON.parse($.cookie(namespace));
} catch (err) { }
if (!data || !data['storage'] || !data['keys']) {
return _getEmptyStorage();
}
return data;
};
/**
* Clears cookies that has flag `session` equals true.
*
* @private
*/
var _clearSession = function () {
Storage['keys'] = $.grep(Storage['keys'], function(key_name, index) {
if (Storage['storage'][key_name]['session']) {
delete Storage['storage'][key_name];
return false;
}
return true;
});
$.cookie(namespace, JSON.stringify(Storage), {
expires: -1,
path: '/'
});
};
/**
* Adds new value to the storage or rewrites existent.
*
* @param {string} name Identifier of the data.
* @param {any} value Data to store.
* @param {boolean} useSession Data with this flag will be removed on
* window unload.
*/
var setItem = function (name, value, useSession) {
if (name) {
if ($.inArray(name, Storage['keys']) === -1) {
Storage['keys'].push(name);
}
Storage['storage'][name] = {
value: value,
session: useSession ? true : false
};
$.cookie(namespace, JSON.stringify(Storage), {
expires: 3650,
path: '/'
});
}
};
/**
* Returns the current value associated with the given name.
*
* @param {string} name Identifier of the data.
* @return {any} The current value associated with the given name.
* If the given key does not exist in the list
* associated with the object then this method must return null.
*/
var getItem = function (name) {
try {
return Storage['storage'][name]['value'];
} catch (err) { }
return null;
};
/**
* Removes the current value associated with the given name.
*
* @param {string} name Identifier of the data.
*/
var removeItem = function (name) {
delete Storage['storage'][name];
Storage['keys'] = $.grep(Storage['keys'], function(key_name, index) {
return name !== key_name;
});
$.cookie(namespace, JSON.stringify(Storage), {
expires: 3650,
path: '/'
});
};
/**
* Empties the storage.
*
*/
var clear = function () {
Storage = _getEmptyStorage();
$.cookie(namespace, null, {
expires: -1,
path: '/'
});
};
/**
* Returns the name of the `n`th key in the list.
*
* @param {number} n Index of the key.
* @return {string} Name of the `n`th key in the list.
* If `n` is greater than or equal to the number of key/value pairs
* in the object, then this method must return `null`.
*/
var key = function (n) {
if (n >= Storage['keys'].length) {
return null;
}
return Storage['keys'][n];
};
/**
* Initializes the module: creates a storage with proper namespace, binds
* `unload` event.
*
* @private
*/
(function initialize() {
if (!namespace) {
namespace = 'cookieStorage';
}
Storage = _getData(namespace);
$(window).unload(_clearSession);
}());
return {
clear: clear,
getItem: getItem,
key: key,
removeItem: removeItem,
setItem: setItem
};
};
return CookieStorage;
});
}(RequireJS.requirejs, RequireJS.require, RequireJS.define));

View File

@@ -25,7 +25,6 @@ from xmodule.x_module import XModule
from xmodule.editing_module import TabsEditingDescriptor
from xmodule.raw_module import EmptyDataRawDescriptor
from xmodule.xml_module import is_pointer_tag, name_to_pathname, deserialize_field
from xmodule.modulestore import Location
from xblock.fields import Scope, String, Boolean, List, Integer, ScopeIds
from xmodule.fields import RelativeTime
@@ -134,8 +133,11 @@ class VideoModule(VideoFields, XModule):
video_time = 0
icon_class = 'video'
# To make sure that js files are called in proper order we use numerical
# index. We do that to avoid issues that occurs in tests.
js = {
'js': [
resource_string(__name__, 'js/src/video/00_cookie_storage.js'),
resource_string(__name__, 'js/src/video/00_resizer.js'),
resource_string(__name__, 'js/src/video/01_initialize.js'),
resource_string(__name__, 'js/src/video/025_focus_grabber.js'),