mirror of
https://github.com/Esri/offline-editor-js.git
synced 2026-02-07 14:26:22 +00:00
added 1st batch of unit tests
This commit is contained in:
parent
45438b7185
commit
5e843fa80a
39
README.md
39
README.md
@ -62,7 +62,44 @@ The library provides a constructor that can simply be used in place of the tradi
|
||||
|
||||
|
||||
|
||||
##Testing
|
||||
Run Jasmine's SpecRunner.html in a browser. You can find it in the /test directory.
|
||||
|
||||
|
||||
##Dependencies
|
||||
ArcGIS API for JavaScript
|
||||
* ArcGIS API for JavaScript
|
||||
|
||||
## Resources
|
||||
|
||||
* [ArcGIS Developers](http://developers.arcgis.com)
|
||||
* [ArcGIS REST Services](http://resources.arcgis.com/en/help/arcgis-rest-api/)
|
||||
* [twitter@esri](http://twitter.com/esri)
|
||||
|
||||
## Issues
|
||||
|
||||
Find a bug or want to request a new feature? Please let us know by submitting an issue.
|
||||
|
||||
## Contributing
|
||||
|
||||
Esri welcomes contributions from anyone and everyone. Please see our [guidelines for contributing](https://github.com/esri/contributing).
|
||||
|
||||
|
||||
## Licensing
|
||||
Copyright 2013 Esri
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
[](Esri Tags: ArcGIS Web Mapping Editing FeatureServices Offline)
|
||||
[](Esri Language: JavaScript)
|
||||
|
||||
|
||||
|
||||
36
scripts/OfflineStore.js → src/OfflineStore.js
Normal file → Executable file
36
scripts/OfflineStore.js → src/OfflineStore.js
Normal file → Executable file
@ -22,7 +22,13 @@ var OfflineStore = function(/* Map */ map) {
|
||||
this.isTimer = null;
|
||||
this.layers = []; //An array of all feature layers
|
||||
this.map = map;
|
||||
this.map.offlineStore = this;
|
||||
if(map != null) {
|
||||
this.map.offlineStore = this
|
||||
}
|
||||
else{
|
||||
console.log("map is null")
|
||||
throw("map is null");
|
||||
}
|
||||
|
||||
/**
|
||||
* Public ENUMs (Constants)
|
||||
@ -50,15 +56,15 @@ var OfflineStore = function(/* Map */ map) {
|
||||
this._localEnum = (function(){
|
||||
var values = {
|
||||
VALIDATION_URL : "http://localhost/offline/test.html", /* Change this to a remote server for testing! */
|
||||
TIMER_URL : "./scripts/Timer.js", /* For use within a child process only */
|
||||
TIMER_URL : "./src/Timer.js", /* For use within a child process only */
|
||||
STORAGE_KEY : "___EsriOfflineStore___", /* Unique key for setting/retrieving values from localStorage */
|
||||
INDEX_KEY : "___EsriOfflineIndex___", /* Index for tracking each action (add, delete, update) in local store */
|
||||
VALIDATION_TIMEOUT : 10 * 1000, /* HTTP timeout when trying to validate internet on/off */
|
||||
LOCAL_STORAGE_MAX_LIMIT : 4.75 /* MB */, /* Most browsers offer default storage of ~5MB */
|
||||
TOKEN : "|||", /* A unique token for tokenizing stringified localStorage values */
|
||||
REQUIRED_LIBS : [
|
||||
"./scripts/Hydrate.js",
|
||||
"./scripts/Poller.js"
|
||||
"./src/Hydrate.js",
|
||||
"./src/Poller.js"
|
||||
]
|
||||
}
|
||||
|
||||
@ -415,7 +421,14 @@ console.log(localStore.toString());
|
||||
*/
|
||||
this._deleteStore = function(){
|
||||
console.log("deleting localStore");
|
||||
localStorage.removeItem(this._localEnum().STORAGE_KEY);
|
||||
try{
|
||||
localStorage.removeItem(this._localEnum().STORAGE_KEY);
|
||||
}
|
||||
catch(err){
|
||||
return err.stack;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -451,7 +464,14 @@ console.log(localStore.toString());
|
||||
|
||||
this._deleteLocalStoreIndex = function(){
|
||||
console.log("deleting localStoreIndex");
|
||||
localStorage.removeItem(this._localEnum().INDEX_KEY);
|
||||
try{
|
||||
localStorage.removeItem(this._localEnum().INDEX_KEY);
|
||||
}
|
||||
catch(err){
|
||||
return err.stack;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -611,7 +631,7 @@ console.log(localStore.toString());
|
||||
//////////////////////////
|
||||
|
||||
/**
|
||||
* Load scripts
|
||||
* Load src
|
||||
* TO-DO: Needs to be made AMD compliant!
|
||||
* @param urlArray
|
||||
* @param callback
|
||||
@ -668,7 +688,7 @@ console.log(localStore.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the OfflineStore library. Loads required scripts. Kicks off timer if
|
||||
* Initializes the OfflineStore library. Loads required src. Kicks off timer if
|
||||
* localStore is not empty.
|
||||
* @see Required script sare set in _localEnum.
|
||||
* @type {*}
|
||||
88
test/jasmine-standalone-1.3.1/SpecRunner.html
Executable file
88
test/jasmine-standalone-1.3.1/SpecRunner.html
Executable file
@ -0,0 +1,88 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
|
||||
"http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>Jasmine Spec Runner</title>
|
||||
|
||||
<link rel="shortcut icon" type="image/png" href="lib/jasmine-1.3.1/jasmine_favicon.png">
|
||||
<link rel="stylesheet" type="text/css" href="lib/jasmine-1.3.1/jasmine.css">
|
||||
<script type="text/javascript" src="lib/jasmine-1.3.1/jasmine.js"></script>
|
||||
<script type="text/javascript" src="lib/jasmine-1.3.1/jasmine-html.js"></script>
|
||||
<link rel="stylesheet" href="http://js.arcgis.com/3.6/js/dojo/dijit/themes/claro/claro.css">
|
||||
<link rel="stylesheet" href="http://js.arcgis.com/3.6/js/esri/css/esri.css">
|
||||
<script src="http://js.arcgis.com/3.6/"></script>
|
||||
|
||||
<!-- include source files here... -->
|
||||
<script type="text/javascript" src="src/OfflineStore.js"></script>
|
||||
<script type="text/javascript" src="src/Poller.js"></script>
|
||||
<script type="text/javascript" src="src/Hydrate.js"></script>
|
||||
|
||||
<!-- include spec files here... -->
|
||||
<script type="text/javascript" src="spec/OfflineStoreSpec.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
var map;
|
||||
var offlineStore;
|
||||
var vertices;
|
||||
var updateFlag = false;
|
||||
|
||||
require(["esri/map","esri/layers/FeatureLayer", "dojo/domReady!"], function(Map,FeatureLayer) {
|
||||
|
||||
map = new Map("map", {
|
||||
basemap: "topo",
|
||||
center: [-83.244, 42.581],
|
||||
zoom: 13,
|
||||
sliderStyle: "small"
|
||||
});
|
||||
|
||||
map.on("layer-add-result", test);
|
||||
|
||||
|
||||
var landusePointLayer = new FeatureLayer("http://sampleserver6.arcgisonline.com/arcgis/rest/services/Military/FeatureServer/6", {
|
||||
mode: FeatureLayer.MODE_SNAPSHOT,
|
||||
outFields: ["*"]
|
||||
});
|
||||
|
||||
map.addLayer(landusePointLayer);
|
||||
|
||||
|
||||
});
|
||||
|
||||
function test(evt){
|
||||
if(evt.layer.hasOwnProperty("type") && evt.layer.type == "Feature Layer"){
|
||||
|
||||
offlineStore = new OfflineStore(map);
|
||||
|
||||
var jasmineEnv = this.jasmine.getEnv();
|
||||
jasmineEnv.updateInterval = 1000;
|
||||
|
||||
var htmlReporter = new jasmine.HtmlReporter();
|
||||
|
||||
jasmineEnv.addReporter(htmlReporter);
|
||||
|
||||
jasmineEnv.specFilter = function(spec) {
|
||||
return htmlReporter.specFilter(spec);
|
||||
};
|
||||
|
||||
var currentWindowOnload = window.onload;
|
||||
|
||||
window.onload = function() {
|
||||
if (currentWindowOnload) {
|
||||
currentWindowOnload();
|
||||
}
|
||||
execJasmine();
|
||||
};
|
||||
|
||||
function execJasmine() {
|
||||
jasmineEnv.execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="map" style=" position: absolute; bottom: 0; left: 0; height:30px; width: 800px;"></div>
|
||||
</body>
|
||||
</html>
|
||||
20
test/jasmine-standalone-1.3.1/lib/jasmine-1.3.1/MIT.LICENSE
Executable file
20
test/jasmine-standalone-1.3.1/lib/jasmine-1.3.1/MIT.LICENSE
Executable file
@ -0,0 +1,20 @@
|
||||
Copyright (c) 2008-2011 Pivotal Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
681
test/jasmine-standalone-1.3.1/lib/jasmine-1.3.1/jasmine-html.js
Executable file
681
test/jasmine-standalone-1.3.1/lib/jasmine-1.3.1/jasmine-html.js
Executable file
@ -0,0 +1,681 @@
|
||||
jasmine.HtmlReporterHelpers = {};
|
||||
|
||||
jasmine.HtmlReporterHelpers.createDom = function(type, attrs, childrenVarArgs) {
|
||||
var el = document.createElement(type);
|
||||
|
||||
for (var i = 2; i < arguments.length; i++) {
|
||||
var child = arguments[i];
|
||||
|
||||
if (typeof child === 'string') {
|
||||
el.appendChild(document.createTextNode(child));
|
||||
} else {
|
||||
if (child) {
|
||||
el.appendChild(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var attr in attrs) {
|
||||
if (attr == "className") {
|
||||
el[attr] = attrs[attr];
|
||||
} else {
|
||||
el.setAttribute(attr, attrs[attr]);
|
||||
}
|
||||
}
|
||||
|
||||
return el;
|
||||
};
|
||||
|
||||
jasmine.HtmlReporterHelpers.getSpecStatus = function(child) {
|
||||
var results = child.results();
|
||||
var status = results.passed() ? 'passed' : 'failed';
|
||||
if (results.skipped) {
|
||||
status = 'skipped';
|
||||
}
|
||||
|
||||
return status;
|
||||
};
|
||||
|
||||
jasmine.HtmlReporterHelpers.appendToSummary = function(child, childElement) {
|
||||
var parentDiv = this.dom.summary;
|
||||
var parentSuite = (typeof child.parentSuite == 'undefined') ? 'suite' : 'parentSuite';
|
||||
var parent = child[parentSuite];
|
||||
|
||||
if (parent) {
|
||||
if (typeof this.views.suites[parent.id] == 'undefined') {
|
||||
this.views.suites[parent.id] = new jasmine.HtmlReporter.SuiteView(parent, this.dom, this.views);
|
||||
}
|
||||
parentDiv = this.views.suites[parent.id].element;
|
||||
}
|
||||
|
||||
parentDiv.appendChild(childElement);
|
||||
};
|
||||
|
||||
|
||||
jasmine.HtmlReporterHelpers.addHelpers = function(ctor) {
|
||||
for(var fn in jasmine.HtmlReporterHelpers) {
|
||||
ctor.prototype[fn] = jasmine.HtmlReporterHelpers[fn];
|
||||
}
|
||||
};
|
||||
|
||||
jasmine.HtmlReporter = function(_doc) {
|
||||
var self = this;
|
||||
var doc = _doc || window.document;
|
||||
|
||||
var reporterView;
|
||||
|
||||
var dom = {};
|
||||
|
||||
// Jasmine Reporter Public Interface
|
||||
self.logRunningSpecs = false;
|
||||
|
||||
self.reportRunnerStarting = function(runner) {
|
||||
var specs = runner.specs() || [];
|
||||
|
||||
if (specs.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
createReporterDom(runner.env.versionString());
|
||||
doc.body.appendChild(dom.reporter);
|
||||
setExceptionHandling();
|
||||
|
||||
reporterView = new jasmine.HtmlReporter.ReporterView(dom);
|
||||
reporterView.addSpecs(specs, self.specFilter);
|
||||
};
|
||||
|
||||
self.reportRunnerResults = function(runner) {
|
||||
reporterView && reporterView.complete();
|
||||
};
|
||||
|
||||
self.reportSuiteResults = function(suite) {
|
||||
reporterView.suiteComplete(suite);
|
||||
};
|
||||
|
||||
self.reportSpecStarting = function(spec) {
|
||||
if (self.logRunningSpecs) {
|
||||
self.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
|
||||
}
|
||||
};
|
||||
|
||||
self.reportSpecResults = function(spec) {
|
||||
reporterView.specComplete(spec);
|
||||
};
|
||||
|
||||
self.log = function() {
|
||||
var console = jasmine.getGlobal().console;
|
||||
if (console && console.log) {
|
||||
if (console.log.apply) {
|
||||
console.log.apply(console, arguments);
|
||||
} else {
|
||||
console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
self.specFilter = function(spec) {
|
||||
if (!focusedSpecName()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return spec.getFullName().indexOf(focusedSpecName()) === 0;
|
||||
};
|
||||
|
||||
return self;
|
||||
|
||||
function focusedSpecName() {
|
||||
var specName;
|
||||
|
||||
(function memoizeFocusedSpec() {
|
||||
if (specName) {
|
||||
return;
|
||||
}
|
||||
|
||||
var paramMap = [];
|
||||
var params = jasmine.HtmlReporter.parameters(doc);
|
||||
|
||||
for (var i = 0; i < params.length; i++) {
|
||||
var p = params[i].split('=');
|
||||
paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
|
||||
}
|
||||
|
||||
specName = paramMap.spec;
|
||||
})();
|
||||
|
||||
return specName;
|
||||
}
|
||||
|
||||
function createReporterDom(version) {
|
||||
dom.reporter = self.createDom('div', { id: 'HTMLReporter', className: 'jasmine_reporter' },
|
||||
dom.banner = self.createDom('div', { className: 'banner' },
|
||||
self.createDom('span', { className: 'title' }, "Jasmine "),
|
||||
self.createDom('span', { className: 'version' }, version)),
|
||||
|
||||
dom.symbolSummary = self.createDom('ul', {className: 'symbolSummary'}),
|
||||
dom.alert = self.createDom('div', {className: 'alert'},
|
||||
self.createDom('span', { className: 'exceptions' },
|
||||
self.createDom('label', { className: 'label', 'for': 'no_try_catch' }, 'No try/catch'),
|
||||
self.createDom('input', { id: 'no_try_catch', type: 'checkbox' }))),
|
||||
dom.results = self.createDom('div', {className: 'results'},
|
||||
dom.summary = self.createDom('div', { className: 'summary' }),
|
||||
dom.details = self.createDom('div', { id: 'details' }))
|
||||
);
|
||||
}
|
||||
|
||||
function noTryCatch() {
|
||||
return window.location.search.match(/catch=false/);
|
||||
}
|
||||
|
||||
function searchWithCatch() {
|
||||
var params = jasmine.HtmlReporter.parameters(window.document);
|
||||
var removed = false;
|
||||
var i = 0;
|
||||
|
||||
while (!removed && i < params.length) {
|
||||
if (params[i].match(/catch=/)) {
|
||||
params.splice(i, 1);
|
||||
removed = true;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (jasmine.CATCH_EXCEPTIONS) {
|
||||
params.push("catch=false");
|
||||
}
|
||||
|
||||
return params.join("&");
|
||||
}
|
||||
|
||||
function setExceptionHandling() {
|
||||
var chxCatch = document.getElementById('no_try_catch');
|
||||
|
||||
if (noTryCatch()) {
|
||||
chxCatch.setAttribute('checked', true);
|
||||
jasmine.CATCH_EXCEPTIONS = false;
|
||||
}
|
||||
chxCatch.onclick = function() {
|
||||
window.location.search = searchWithCatch();
|
||||
};
|
||||
}
|
||||
};
|
||||
jasmine.HtmlReporter.parameters = function(doc) {
|
||||
var paramStr = doc.location.search.substring(1);
|
||||
var params = [];
|
||||
|
||||
if (paramStr.length > 0) {
|
||||
params = paramStr.split('&');
|
||||
}
|
||||
return params;
|
||||
}
|
||||
jasmine.HtmlReporter.sectionLink = function(sectionName) {
|
||||
var link = '?';
|
||||
var params = [];
|
||||
|
||||
if (sectionName) {
|
||||
params.push('spec=' + encodeURIComponent(sectionName));
|
||||
}
|
||||
if (!jasmine.CATCH_EXCEPTIONS) {
|
||||
params.push("catch=false");
|
||||
}
|
||||
if (params.length > 0) {
|
||||
link += params.join("&");
|
||||
}
|
||||
|
||||
return link;
|
||||
};
|
||||
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter);
|
||||
jasmine.HtmlReporter.ReporterView = function(dom) {
|
||||
this.startedAt = new Date();
|
||||
this.runningSpecCount = 0;
|
||||
this.completeSpecCount = 0;
|
||||
this.passedCount = 0;
|
||||
this.failedCount = 0;
|
||||
this.skippedCount = 0;
|
||||
|
||||
this.createResultsMenu = function() {
|
||||
this.resultsMenu = this.createDom('span', {className: 'resultsMenu bar'},
|
||||
this.summaryMenuItem = this.createDom('a', {className: 'summaryMenuItem', href: "#"}, '0 specs'),
|
||||
' | ',
|
||||
this.detailsMenuItem = this.createDom('a', {className: 'detailsMenuItem', href: "#"}, '0 failing'));
|
||||
|
||||
this.summaryMenuItem.onclick = function() {
|
||||
dom.reporter.className = dom.reporter.className.replace(/ showDetails/g, '');
|
||||
};
|
||||
|
||||
this.detailsMenuItem.onclick = function() {
|
||||
showDetails();
|
||||
};
|
||||
};
|
||||
|
||||
this.addSpecs = function(specs, specFilter) {
|
||||
this.totalSpecCount = specs.length;
|
||||
|
||||
this.views = {
|
||||
specs: {},
|
||||
suites: {}
|
||||
};
|
||||
|
||||
for (var i = 0; i < specs.length; i++) {
|
||||
var spec = specs[i];
|
||||
this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom, this.views);
|
||||
if (specFilter(spec)) {
|
||||
this.runningSpecCount++;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.specComplete = function(spec) {
|
||||
this.completeSpecCount++;
|
||||
|
||||
if (isUndefined(this.views.specs[spec.id])) {
|
||||
this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom);
|
||||
}
|
||||
|
||||
var specView = this.views.specs[spec.id];
|
||||
|
||||
switch (specView.status()) {
|
||||
case 'passed':
|
||||
this.passedCount++;
|
||||
break;
|
||||
|
||||
case 'failed':
|
||||
this.failedCount++;
|
||||
break;
|
||||
|
||||
case 'skipped':
|
||||
this.skippedCount++;
|
||||
break;
|
||||
}
|
||||
|
||||
specView.refresh();
|
||||
this.refresh();
|
||||
};
|
||||
|
||||
this.suiteComplete = function(suite) {
|
||||
var suiteView = this.views.suites[suite.id];
|
||||
if (isUndefined(suiteView)) {
|
||||
return;
|
||||
}
|
||||
suiteView.refresh();
|
||||
};
|
||||
|
||||
this.refresh = function() {
|
||||
|
||||
if (isUndefined(this.resultsMenu)) {
|
||||
this.createResultsMenu();
|
||||
}
|
||||
|
||||
// currently running UI
|
||||
if (isUndefined(this.runningAlert)) {
|
||||
this.runningAlert = this.createDom('a', { href: jasmine.HtmlReporter.sectionLink(), className: "runningAlert bar" });
|
||||
dom.alert.appendChild(this.runningAlert);
|
||||
}
|
||||
this.runningAlert.innerHTML = "Running " + this.completeSpecCount + " of " + specPluralizedFor(this.totalSpecCount);
|
||||
|
||||
// skipped specs UI
|
||||
if (isUndefined(this.skippedAlert)) {
|
||||
this.skippedAlert = this.createDom('a', { href: jasmine.HtmlReporter.sectionLink(), className: "skippedAlert bar" });
|
||||
}
|
||||
|
||||
this.skippedAlert.innerHTML = "Skipping " + this.skippedCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all";
|
||||
|
||||
if (this.skippedCount === 1 && isDefined(dom.alert)) {
|
||||
dom.alert.appendChild(this.skippedAlert);
|
||||
}
|
||||
|
||||
// passing specs UI
|
||||
if (isUndefined(this.passedAlert)) {
|
||||
this.passedAlert = this.createDom('span', { href: jasmine.HtmlReporter.sectionLink(), className: "passingAlert bar" });
|
||||
}
|
||||
this.passedAlert.innerHTML = "Passing " + specPluralizedFor(this.passedCount);
|
||||
|
||||
// failing specs UI
|
||||
if (isUndefined(this.failedAlert)) {
|
||||
this.failedAlert = this.createDom('span', {href: "?", className: "failingAlert bar"});
|
||||
}
|
||||
this.failedAlert.innerHTML = "Failing " + specPluralizedFor(this.failedCount);
|
||||
|
||||
if (this.failedCount === 1 && isDefined(dom.alert)) {
|
||||
dom.alert.appendChild(this.failedAlert);
|
||||
dom.alert.appendChild(this.resultsMenu);
|
||||
}
|
||||
|
||||
// summary info
|
||||
this.summaryMenuItem.innerHTML = "" + specPluralizedFor(this.runningSpecCount);
|
||||
this.detailsMenuItem.innerHTML = "" + this.failedCount + " failing";
|
||||
};
|
||||
|
||||
this.complete = function() {
|
||||
dom.alert.removeChild(this.runningAlert);
|
||||
|
||||
this.skippedAlert.innerHTML = "Ran " + this.runningSpecCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all";
|
||||
|
||||
if (this.failedCount === 0) {
|
||||
dom.alert.appendChild(this.createDom('span', {className: 'passingAlert bar'}, "Passing " + specPluralizedFor(this.passedCount)));
|
||||
} else {
|
||||
showDetails();
|
||||
}
|
||||
|
||||
dom.banner.appendChild(this.createDom('span', {className: 'duration'}, "finished in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s"));
|
||||
};
|
||||
|
||||
return this;
|
||||
|
||||
function showDetails() {
|
||||
if (dom.reporter.className.search(/showDetails/) === -1) {
|
||||
dom.reporter.className += " showDetails";
|
||||
}
|
||||
}
|
||||
|
||||
function isUndefined(obj) {
|
||||
return typeof obj === 'undefined';
|
||||
}
|
||||
|
||||
function isDefined(obj) {
|
||||
return !isUndefined(obj);
|
||||
}
|
||||
|
||||
function specPluralizedFor(count) {
|
||||
var str = count + " spec";
|
||||
if (count > 1) {
|
||||
str += "s"
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.ReporterView);
|
||||
|
||||
|
||||
jasmine.HtmlReporter.SpecView = function(spec, dom, views) {
|
||||
this.spec = spec;
|
||||
this.dom = dom;
|
||||
this.views = views;
|
||||
|
||||
this.symbol = this.createDom('li', { className: 'pending' });
|
||||
this.dom.symbolSummary.appendChild(this.symbol);
|
||||
|
||||
this.summary = this.createDom('div', { className: 'specSummary' },
|
||||
this.createDom('a', {
|
||||
className: 'description',
|
||||
href: jasmine.HtmlReporter.sectionLink(this.spec.getFullName()),
|
||||
title: this.spec.getFullName()
|
||||
}, this.spec.description)
|
||||
);
|
||||
|
||||
this.detail = this.createDom('div', { className: 'specDetail' },
|
||||
this.createDom('a', {
|
||||
className: 'description',
|
||||
href: '?spec=' + encodeURIComponent(this.spec.getFullName()),
|
||||
title: this.spec.getFullName()
|
||||
}, this.spec.getFullName())
|
||||
);
|
||||
};
|
||||
|
||||
jasmine.HtmlReporter.SpecView.prototype.status = function() {
|
||||
return this.getSpecStatus(this.spec);
|
||||
};
|
||||
|
||||
jasmine.HtmlReporter.SpecView.prototype.refresh = function() {
|
||||
this.symbol.className = this.status();
|
||||
|
||||
switch (this.status()) {
|
||||
case 'skipped':
|
||||
break;
|
||||
|
||||
case 'passed':
|
||||
this.appendSummaryToSuiteDiv();
|
||||
break;
|
||||
|
||||
case 'failed':
|
||||
this.appendSummaryToSuiteDiv();
|
||||
this.appendFailureDetail();
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
jasmine.HtmlReporter.SpecView.prototype.appendSummaryToSuiteDiv = function() {
|
||||
this.summary.className += ' ' + this.status();
|
||||
this.appendToSummary(this.spec, this.summary);
|
||||
};
|
||||
|
||||
jasmine.HtmlReporter.SpecView.prototype.appendFailureDetail = function() {
|
||||
this.detail.className += ' ' + this.status();
|
||||
|
||||
var resultItems = this.spec.results().getItems();
|
||||
var messagesDiv = this.createDom('div', { className: 'messages' });
|
||||
|
||||
for (var i = 0; i < resultItems.length; i++) {
|
||||
var result = resultItems[i];
|
||||
|
||||
if (result.type == 'log') {
|
||||
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
|
||||
} else if (result.type == 'expect' && result.passed && !result.passed()) {
|
||||
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
|
||||
|
||||
if (result.trace.stack) {
|
||||
messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (messagesDiv.childNodes.length > 0) {
|
||||
this.detail.appendChild(messagesDiv);
|
||||
this.dom.details.appendChild(this.detail);
|
||||
}
|
||||
};
|
||||
|
||||
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SpecView);jasmine.HtmlReporter.SuiteView = function(suite, dom, views) {
|
||||
this.suite = suite;
|
||||
this.dom = dom;
|
||||
this.views = views;
|
||||
|
||||
this.element = this.createDom('div', { className: 'suite' },
|
||||
this.createDom('a', { className: 'description', href: jasmine.HtmlReporter.sectionLink(this.suite.getFullName()) }, this.suite.description)
|
||||
);
|
||||
|
||||
this.appendToSummary(this.suite, this.element);
|
||||
};
|
||||
|
||||
jasmine.HtmlReporter.SuiteView.prototype.status = function() {
|
||||
return this.getSpecStatus(this.suite);
|
||||
};
|
||||
|
||||
jasmine.HtmlReporter.SuiteView.prototype.refresh = function() {
|
||||
this.element.className += " " + this.status();
|
||||
};
|
||||
|
||||
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SuiteView);
|
||||
|
||||
/* @deprecated Use jasmine.HtmlReporter instead
|
||||
*/
|
||||
jasmine.TrivialReporter = function(doc) {
|
||||
this.document = doc || document;
|
||||
this.suiteDivs = {};
|
||||
this.logRunningSpecs = false;
|
||||
};
|
||||
|
||||
jasmine.TrivialReporter.prototype.createDom = function(type, attrs, childrenVarArgs) {
|
||||
var el = document.createElement(type);
|
||||
|
||||
for (var i = 2; i < arguments.length; i++) {
|
||||
var child = arguments[i];
|
||||
|
||||
if (typeof child === 'string') {
|
||||
el.appendChild(document.createTextNode(child));
|
||||
} else {
|
||||
if (child) { el.appendChild(child); }
|
||||
}
|
||||
}
|
||||
|
||||
for (var attr in attrs) {
|
||||
if (attr == "className") {
|
||||
el[attr] = attrs[attr];
|
||||
} else {
|
||||
el.setAttribute(attr, attrs[attr]);
|
||||
}
|
||||
}
|
||||
|
||||
return el;
|
||||
};
|
||||
|
||||
jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) {
|
||||
var showPassed, showSkipped;
|
||||
|
||||
this.outerDiv = this.createDom('div', { id: 'TrivialReporter', className: 'jasmine_reporter' },
|
||||
this.createDom('div', { className: 'banner' },
|
||||
this.createDom('div', { className: 'logo' },
|
||||
this.createDom('span', { className: 'title' }, "Jasmine"),
|
||||
this.createDom('span', { className: 'version' }, runner.env.versionString())),
|
||||
this.createDom('div', { className: 'options' },
|
||||
"Show ",
|
||||
showPassed = this.createDom('input', { id: "__jasmine_TrivialReporter_showPassed__", type: 'checkbox' }),
|
||||
this.createDom('label', { "for": "__jasmine_TrivialReporter_showPassed__" }, " passed "),
|
||||
showSkipped = this.createDom('input', { id: "__jasmine_TrivialReporter_showSkipped__", type: 'checkbox' }),
|
||||
this.createDom('label', { "for": "__jasmine_TrivialReporter_showSkipped__" }, " skipped")
|
||||
)
|
||||
),
|
||||
|
||||
this.runnerDiv = this.createDom('div', { className: 'runner running' },
|
||||
this.createDom('a', { className: 'run_spec', href: '?' }, "run all"),
|
||||
this.runnerMessageSpan = this.createDom('span', {}, "Running..."),
|
||||
this.finishedAtSpan = this.createDom('span', { className: 'finished-at' }, ""))
|
||||
);
|
||||
|
||||
this.document.body.appendChild(this.outerDiv);
|
||||
|
||||
var suites = runner.suites();
|
||||
for (var i = 0; i < suites.length; i++) {
|
||||
var suite = suites[i];
|
||||
var suiteDiv = this.createDom('div', { className: 'suite' },
|
||||
this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, "run"),
|
||||
this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, suite.description));
|
||||
this.suiteDivs[suite.id] = suiteDiv;
|
||||
var parentDiv = this.outerDiv;
|
||||
if (suite.parentSuite) {
|
||||
parentDiv = this.suiteDivs[suite.parentSuite.id];
|
||||
}
|
||||
parentDiv.appendChild(suiteDiv);
|
||||
}
|
||||
|
||||
this.startedAt = new Date();
|
||||
|
||||
var self = this;
|
||||
showPassed.onclick = function(evt) {
|
||||
if (showPassed.checked) {
|
||||
self.outerDiv.className += ' show-passed';
|
||||
} else {
|
||||
self.outerDiv.className = self.outerDiv.className.replace(/ show-passed/, '');
|
||||
}
|
||||
};
|
||||
|
||||
showSkipped.onclick = function(evt) {
|
||||
if (showSkipped.checked) {
|
||||
self.outerDiv.className += ' show-skipped';
|
||||
} else {
|
||||
self.outerDiv.className = self.outerDiv.className.replace(/ show-skipped/, '');
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
jasmine.TrivialReporter.prototype.reportRunnerResults = function(runner) {
|
||||
var results = runner.results();
|
||||
var className = (results.failedCount > 0) ? "runner failed" : "runner passed";
|
||||
this.runnerDiv.setAttribute("class", className);
|
||||
//do it twice for IE
|
||||
this.runnerDiv.setAttribute("className", className);
|
||||
var specs = runner.specs();
|
||||
var specCount = 0;
|
||||
for (var i = 0; i < specs.length; i++) {
|
||||
if (this.specFilter(specs[i])) {
|
||||
specCount++;
|
||||
}
|
||||
}
|
||||
var message = "" + specCount + " spec" + (specCount == 1 ? "" : "s" ) + ", " + results.failedCount + " failure" + ((results.failedCount == 1) ? "" : "s");
|
||||
message += " in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s";
|
||||
this.runnerMessageSpan.replaceChild(this.createDom('a', { className: 'description', href: '?'}, message), this.runnerMessageSpan.firstChild);
|
||||
|
||||
this.finishedAtSpan.appendChild(document.createTextNode("Finished at " + new Date().toString()));
|
||||
};
|
||||
|
||||
jasmine.TrivialReporter.prototype.reportSuiteResults = function(suite) {
|
||||
var results = suite.results();
|
||||
var status = results.passed() ? 'passed' : 'failed';
|
||||
if (results.totalCount === 0) { // todo: change this to check results.skipped
|
||||
status = 'skipped';
|
||||
}
|
||||
this.suiteDivs[suite.id].className += " " + status;
|
||||
};
|
||||
|
||||
jasmine.TrivialReporter.prototype.reportSpecStarting = function(spec) {
|
||||
if (this.logRunningSpecs) {
|
||||
this.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
|
||||
}
|
||||
};
|
||||
|
||||
jasmine.TrivialReporter.prototype.reportSpecResults = function(spec) {
|
||||
var results = spec.results();
|
||||
var status = results.passed() ? 'passed' : 'failed';
|
||||
if (results.skipped) {
|
||||
status = 'skipped';
|
||||
}
|
||||
var specDiv = this.createDom('div', { className: 'spec ' + status },
|
||||
this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(spec.getFullName()) }, "run"),
|
||||
this.createDom('a', {
|
||||
className: 'description',
|
||||
href: '?spec=' + encodeURIComponent(spec.getFullName()),
|
||||
title: spec.getFullName()
|
||||
}, spec.description));
|
||||
|
||||
|
||||
var resultItems = results.getItems();
|
||||
var messagesDiv = this.createDom('div', { className: 'messages' });
|
||||
for (var i = 0; i < resultItems.length; i++) {
|
||||
var result = resultItems[i];
|
||||
|
||||
if (result.type == 'log') {
|
||||
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
|
||||
} else if (result.type == 'expect' && result.passed && !result.passed()) {
|
||||
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
|
||||
|
||||
if (result.trace.stack) {
|
||||
messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (messagesDiv.childNodes.length > 0) {
|
||||
specDiv.appendChild(messagesDiv);
|
||||
}
|
||||
|
||||
this.suiteDivs[spec.suite.id].appendChild(specDiv);
|
||||
};
|
||||
|
||||
jasmine.TrivialReporter.prototype.log = function() {
|
||||
var console = jasmine.getGlobal().console;
|
||||
if (console && console.log) {
|
||||
if (console.log.apply) {
|
||||
console.log.apply(console, arguments);
|
||||
} else {
|
||||
console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
jasmine.TrivialReporter.prototype.getLocation = function() {
|
||||
return this.document.location;
|
||||
};
|
||||
|
||||
jasmine.TrivialReporter.prototype.specFilter = function(spec) {
|
||||
var paramMap = {};
|
||||
var params = this.getLocation().search.substring(1).split('&');
|
||||
for (var i = 0; i < params.length; i++) {
|
||||
var p = params[i].split('=');
|
||||
paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
|
||||
}
|
||||
|
||||
if (!paramMap.spec) {
|
||||
return true;
|
||||
}
|
||||
return spec.getFullName().indexOf(paramMap.spec) === 0;
|
||||
};
|
||||
82
test/jasmine-standalone-1.3.1/lib/jasmine-1.3.1/jasmine.css
Executable file
82
test/jasmine-standalone-1.3.1/lib/jasmine-1.3.1/jasmine.css
Executable file
@ -0,0 +1,82 @@
|
||||
body { background-color: #eeeeee; padding: 0; margin: 5px; overflow-y: scroll; }
|
||||
|
||||
#HTMLReporter { font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333333; }
|
||||
#HTMLReporter a { text-decoration: none; }
|
||||
#HTMLReporter a:hover { text-decoration: underline; }
|
||||
#HTMLReporter p, #HTMLReporter h1, #HTMLReporter h2, #HTMLReporter h3, #HTMLReporter h4, #HTMLReporter h5, #HTMLReporter h6 { margin: 0; line-height: 14px; }
|
||||
#HTMLReporter .banner, #HTMLReporter .symbolSummary, #HTMLReporter .summary, #HTMLReporter .resultMessage, #HTMLReporter .specDetail .description, #HTMLReporter .alert .bar, #HTMLReporter .stackTrace { padding-left: 9px; padding-right: 9px; }
|
||||
#HTMLReporter #jasmine_content { position: fixed; right: 100%; }
|
||||
#HTMLReporter .version { color: #aaaaaa; }
|
||||
#HTMLReporter .banner { margin-top: 14px; }
|
||||
#HTMLReporter .duration { color: #aaaaaa; float: right; }
|
||||
#HTMLReporter .symbolSummary { overflow: hidden; *zoom: 1; margin: 14px 0; }
|
||||
#HTMLReporter .symbolSummary li { display: block; float: left; height: 7px; width: 14px; margin-bottom: 7px; font-size: 16px; }
|
||||
#HTMLReporter .symbolSummary li.passed { font-size: 14px; }
|
||||
#HTMLReporter .symbolSummary li.passed:before { color: #5e7d00; content: "\02022"; }
|
||||
#HTMLReporter .symbolSummary li.failed { line-height: 9px; }
|
||||
#HTMLReporter .symbolSummary li.failed:before { color: #b03911; content: "x"; font-weight: bold; margin-left: -1px; }
|
||||
#HTMLReporter .symbolSummary li.skipped { font-size: 14px; }
|
||||
#HTMLReporter .symbolSummary li.skipped:before { color: #bababa; content: "\02022"; }
|
||||
#HTMLReporter .symbolSummary li.pending { line-height: 11px; }
|
||||
#HTMLReporter .symbolSummary li.pending:before { color: #aaaaaa; content: "-"; }
|
||||
#HTMLReporter .exceptions { color: #fff; float: right; margin-top: 5px; margin-right: 5px; }
|
||||
#HTMLReporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; }
|
||||
#HTMLReporter .runningAlert { background-color: #666666; }
|
||||
#HTMLReporter .skippedAlert { background-color: #aaaaaa; }
|
||||
#HTMLReporter .skippedAlert:first-child { background-color: #333333; }
|
||||
#HTMLReporter .skippedAlert:hover { text-decoration: none; color: white; text-decoration: underline; }
|
||||
#HTMLReporter .passingAlert { background-color: #a6b779; }
|
||||
#HTMLReporter .passingAlert:first-child { background-color: #5e7d00; }
|
||||
#HTMLReporter .failingAlert { background-color: #cf867e; }
|
||||
#HTMLReporter .failingAlert:first-child { background-color: #b03911; }
|
||||
#HTMLReporter .results { margin-top: 14px; }
|
||||
#HTMLReporter #details { display: none; }
|
||||
#HTMLReporter .resultsMenu, #HTMLReporter .resultsMenu a { background-color: #fff; color: #333333; }
|
||||
#HTMLReporter.showDetails .summaryMenuItem { font-weight: normal; text-decoration: inherit; }
|
||||
#HTMLReporter.showDetails .summaryMenuItem:hover { text-decoration: underline; }
|
||||
#HTMLReporter.showDetails .detailsMenuItem { font-weight: bold; text-decoration: underline; }
|
||||
#HTMLReporter.showDetails .summary { display: none; }
|
||||
#HTMLReporter.showDetails #details { display: block; }
|
||||
#HTMLReporter .summaryMenuItem { font-weight: bold; text-decoration: underline; }
|
||||
#HTMLReporter .summary { margin-top: 14px; }
|
||||
#HTMLReporter .summary .suite .suite, #HTMLReporter .summary .specSummary { margin-left: 14px; }
|
||||
#HTMLReporter .summary .specSummary.passed a { color: #5e7d00; }
|
||||
#HTMLReporter .summary .specSummary.failed a { color: #b03911; }
|
||||
#HTMLReporter .description + .suite { margin-top: 0; }
|
||||
#HTMLReporter .suite { margin-top: 14px; }
|
||||
#HTMLReporter .suite a { color: #333333; }
|
||||
#HTMLReporter #details .specDetail { margin-bottom: 28px; }
|
||||
#HTMLReporter #details .specDetail .description { display: block; color: white; background-color: #b03911; }
|
||||
#HTMLReporter .resultMessage { padding-top: 14px; color: #333333; }
|
||||
#HTMLReporter .resultMessage span.result { display: block; }
|
||||
#HTMLReporter .stackTrace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666666; border: 1px solid #ddd; background: white; white-space: pre; }
|
||||
|
||||
#TrivialReporter { padding: 8px 13px; position: absolute; top: 0; bottom: 0; left: 0; right: 0; overflow-y: scroll; background-color: white; font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif; /*.resultMessage {*/ /*white-space: pre;*/ /*}*/ }
|
||||
#TrivialReporter a:visited, #TrivialReporter a { color: #303; }
|
||||
#TrivialReporter a:hover, #TrivialReporter a:active { color: blue; }
|
||||
#TrivialReporter .run_spec { float: right; padding-right: 5px; font-size: .8em; text-decoration: none; }
|
||||
#TrivialReporter .banner { color: #303; background-color: #fef; padding: 5px; }
|
||||
#TrivialReporter .logo { float: left; font-size: 1.1em; padding-left: 5px; }
|
||||
#TrivialReporter .logo .version { font-size: .6em; padding-left: 1em; }
|
||||
#TrivialReporter .runner.running { background-color: yellow; }
|
||||
#TrivialReporter .options { text-align: right; font-size: .8em; }
|
||||
#TrivialReporter .suite { border: 1px outset gray; margin: 5px 0; padding-left: 1em; }
|
||||
#TrivialReporter .suite .suite { margin: 5px; }
|
||||
#TrivialReporter .suite.passed { background-color: #dfd; }
|
||||
#TrivialReporter .suite.failed { background-color: #fdd; }
|
||||
#TrivialReporter .spec { margin: 5px; padding-left: 1em; clear: both; }
|
||||
#TrivialReporter .spec.failed, #TrivialReporter .spec.passed, #TrivialReporter .spec.skipped { padding-bottom: 5px; border: 1px solid gray; }
|
||||
#TrivialReporter .spec.failed { background-color: #fbb; border-color: red; }
|
||||
#TrivialReporter .spec.passed { background-color: #bfb; border-color: green; }
|
||||
#TrivialReporter .spec.skipped { background-color: #bbb; }
|
||||
#TrivialReporter .messages { border-left: 1px dashed gray; padding-left: 1em; padding-right: 1em; }
|
||||
#TrivialReporter .passed { background-color: #cfc; display: none; }
|
||||
#TrivialReporter .failed { background-color: #fbb; }
|
||||
#TrivialReporter .skipped { color: #777; background-color: #eee; display: none; }
|
||||
#TrivialReporter .resultMessage span.result { display: block; line-height: 2em; color: black; }
|
||||
#TrivialReporter .resultMessage .mismatch { color: black; }
|
||||
#TrivialReporter .stackTrace { white-space: pre; font-size: .8em; margin-left: 10px; max-height: 5em; overflow: auto; border: 1px inset red; padding: 1em; background: #eef; }
|
||||
#TrivialReporter .finished-at { padding-left: 1em; font-size: .6em; }
|
||||
#TrivialReporter.show-passed .passed, #TrivialReporter.show-skipped .skipped { display: block; }
|
||||
#TrivialReporter #jasmine_content { position: fixed; right: 100%; }
|
||||
#TrivialReporter .runner { border: 1px solid gray; display: block; margin: 5px 0; padding: 2px 0 2px 10px; }
|
||||
2600
test/jasmine-standalone-1.3.1/lib/jasmine-1.3.1/jasmine.js
Executable file
2600
test/jasmine-standalone-1.3.1/lib/jasmine-1.3.1/jasmine.js
Executable file
File diff suppressed because it is too large
Load Diff
42
test/jasmine-standalone-1.3.1/spec/OfflineStoreSpec.js
Executable file
42
test/jasmine-standalone-1.3.1/spec/OfflineStoreSpec.js
Executable file
@ -0,0 +1,42 @@
|
||||
describe("OfflineStore", function() {
|
||||
|
||||
it("provide local storage in MBs", function() {
|
||||
|
||||
var mb = offlineStore.getlocalStorageUsed();
|
||||
expect(mb).toEqual(jasmine.any(Number));
|
||||
|
||||
}.bind(this));
|
||||
|
||||
it("delete localStore", function() {
|
||||
var store = offlineStore._deleteStore();
|
||||
expect(store).toEqual(true);
|
||||
|
||||
})
|
||||
|
||||
it("retrieve localStore is null", function() {
|
||||
var store = offlineStore.getStore();
|
||||
expect(store).toBeNull();
|
||||
})
|
||||
|
||||
it("delete localStore index", function() {
|
||||
var index = offlineStore._deleteLocalStoreIndex();
|
||||
expect(index).toEqual(true);
|
||||
})
|
||||
|
||||
it("retrieve localStore index is null", function() {
|
||||
var index = offlineStore.getLocalStoreIndex();
|
||||
expect(index).toBeNull();
|
||||
})
|
||||
|
||||
it("check internet", function() {
|
||||
var net = offlineStore._checkInternet();
|
||||
expect(net).toEqual(true);
|
||||
})
|
||||
|
||||
it("validate feature layer available", function() {
|
||||
var layer = offlineStore.layers[0];
|
||||
expect(layer.type).toEqual("Feature Layer");
|
||||
})
|
||||
|
||||
|
||||
});
|
||||
58
test/jasmine-standalone-1.3.1/spec/PlayerSpec.js
Executable file
58
test/jasmine-standalone-1.3.1/spec/PlayerSpec.js
Executable file
@ -0,0 +1,58 @@
|
||||
describe("Player", function() {
|
||||
var player;
|
||||
var song;
|
||||
|
||||
beforeEach(function() {
|
||||
player = new Player();
|
||||
song = new Song();
|
||||
});
|
||||
|
||||
it("should be able to play a Song", function() {
|
||||
player.play(song);
|
||||
expect(player.currentlyPlayingSong).toEqual(song);
|
||||
|
||||
//demonstrates use of custom matcher
|
||||
expect(player).toBePlaying(song);
|
||||
});
|
||||
|
||||
describe("when song has been paused", function() {
|
||||
beforeEach(function() {
|
||||
player.play(song);
|
||||
player.pause();
|
||||
});
|
||||
|
||||
it("should indicate that the song is currently paused", function() {
|
||||
expect(player.isPlaying).toBeFalsy();
|
||||
|
||||
// demonstrates use of 'not' with a custom matcher
|
||||
expect(player).not.toBePlaying(song);
|
||||
});
|
||||
|
||||
it("should be possible to resume", function() {
|
||||
player.resume();
|
||||
expect(player.isPlaying).toBeTruthy();
|
||||
expect(player.currentlyPlayingSong).toEqual(song);
|
||||
});
|
||||
});
|
||||
|
||||
// demonstrates use of spies to intercept and test method calls
|
||||
it("tells the current song if the user has made it a favorite", function() {
|
||||
spyOn(song, 'persistFavoriteStatus');
|
||||
|
||||
player.play(song);
|
||||
player.makeFavorite();
|
||||
|
||||
expect(song.persistFavoriteStatus).toHaveBeenCalledWith(true);
|
||||
});
|
||||
|
||||
//demonstrates use of expected exceptions
|
||||
describe("#resume", function() {
|
||||
it("should throw an exception if song is already playing", function() {
|
||||
player.play(song);
|
||||
|
||||
expect(function() {
|
||||
player.resume();
|
||||
}).toThrow("song is already playing");
|
||||
});
|
||||
});
|
||||
});
|
||||
46
test/jasmine-standalone-1.3.1/src/Hydrate.js
Executable file
46
test/jasmine-standalone-1.3.1/src/Hydrate.js
Executable file
@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Awesome helper library for serializing and deserializing JavaScript Objects
|
||||
* Source is from: https://npmjs.org/package/hydrate
|
||||
*/
|
||||
(function(){if(!Array.prototype.indexOf)
|
||||
{Array.prototype.indexOf=function(elt)
|
||||
{var len=this.length;var from=Number(arguments[1])||0;from=(from<0)?Math.ceil(from):Math.floor(from);if(from<0)
|
||||
from+=len;for(;from<len;from++)
|
||||
{if(from in this&&this[from]===elt)
|
||||
return from;}
|
||||
return-1;};}
|
||||
if(typeof JSON==="undefined"){var dir="../lib";if(typeof JSON2JSDirectory!=="undefined")dir=JSON2JSDirectory;document.write('\x3Cscript type="text/javascript" src="'+ dir+'/json2.js">\x3C/script>');};var ContextResolver,Hydrate,MultiResolver,Resolver;var __hasProp=Object.prototype.hasOwnProperty,__extends=function(child,parent){for(var key in parent){if(__hasProp.call(parent,key))child[key]=parent[key];}
|
||||
function ctor(){this.constructor=child;}
|
||||
ctor.prototype=parent.prototype;child.prototype=new ctor;child.__super__=parent.prototype;return child;};Hydrate=(function(){var Util;Util=(function(){function Util(){}
|
||||
Util.d2h=function(d){return d.toString(16);};Util.h2d=function(h){return parseInt(h,16);};Util.supportsProto={}.__proto__!=null;Util.supportsFunctionNames=typeof(function(){}).name==="string";Util.functionName=function(func){var _ref;if(Util.supportsFunctionNames){return func.name;}else{return(_ref=func.toString().match(/function ([^(]*)/))!=null?_ref[1]:void 0;}};return Util;})();Hydrate.Util=Util;Hydrate.NonPrototypeFunctionError=(function(){__extends(NonPrototypeFunctionError,Error);function NonPrototypeFunctionError(object,name){this.object=object;this.name=name;this.message="Couldn't serialize object; had non-prototype function '"+ this.name+"'";}
|
||||
return NonPrototypeFunctionError;})();Hydrate.PrototypeNotFoundError=(function(){__extends(PrototypeNotFoundError,Error);function PrototypeNotFoundError(object,cons_id){this.object=object;this.cons_id=cons_id;this.message="Prototype not found for object; looked for "+ this.cons_id;}
|
||||
return PrototypeNotFoundError;})();Hydrate.AnonymousConstructorError=(function(){__extends(AnonymousConstructorError,Error);function AnonymousConstructorError(object){this.object=object;this.message="Couldn't resolve constructor name; seems it has an anonymous constructor and object's prototype has no #constructor_name property to provide hints";}
|
||||
return AnonymousConstructorError;})();Hydrate.VersionInstancePropertyError=(function(){__extends(VersionInstancePropertyError,Error);function VersionInstancePropertyError(object){this.object=object;this.message="Objects can't have versions on the instances; can only be on the prototype";}
|
||||
return VersionInstancePropertyError;})();function Hydrate(resolver){this.resolver=resolver!=null?resolver:null;if(!(this.resolver!=null)){this.resolver=new ContextResolver(window);}
|
||||
this.errorHandler=function(e){throw e;};this.migrations={};}
|
||||
Hydrate.prototype.stringify=function(input){var arr,i,result;this.processed_inputs=[];this.counter=0;result=(function(){var _i,_len;switch(typeof input){case"number":case"string":return JSON.stringify(input);case"function":throw new Error("can't serialize functions");break;default:if(input instanceof Array){arr=[];for(_i=0,_len=input.length;_i<_len;_i++){i=input[_i];arr.push(this.analyze(i));}
|
||||
return JSON.stringify(arr);}else{return JSON.stringify(this.analyze(input));}}}).call(this);this.cleanAfterStringify();return result;};Hydrate.prototype.cleanAfterStringify=function(){var input,_i,_len,_ref;_ref=this.processed_inputs;for(_i=0,_len=_ref.length;_i<_len;_i++){input=_ref[_i];if(input){delete input.__hydrate_id;delete input.version;}}
|
||||
return true;};Hydrate.prototype.analyze=function(input,name){var cons,i,k,output,v,_len;switch(typeof input){case"number":case"string":return input;case"function":return this.errorHandler(new Hydrate.NonPrototypeFunctionError(input,name));case"undefined":return"__hydrate_undef";default:if(input===null){return null;}else if(input instanceof Array){output=[];for(i=0,_len=input.length;i<_len;i++){v=input[i];output[i]=this.analyze(v,i);}
|
||||
return output;}else{if(input.__hydrate_id){return"__hydrate_ref_"+ input.__hydrate_id;}else{input.__hydrate_id=Util.d2h(this.counter++);this.processed_inputs.push(input);output=new Object;for(k in input){v=input[k];if(input.hasOwnProperty(k)){output[k]=this.analyze(v,k);}}
|
||||
cons=Util.functionName(input.constructor);if(cons===""&&!input.hasOwnProperty("constructor_name")){cons=input.constructor_name;}
|
||||
if(!(cons!=null)){this.errorHandler(AnonymousConstructorError(input));}
|
||||
if(cons!=="Object"){output.__hydrate_cons=cons;}
|
||||
if(input.hasOwnProperty("version")){this.errorHandler(new Hydrate.VersionInstancePropertyError(input));}
|
||||
if(input.version!=null){output.version=input.version;}
|
||||
return output;}}}};Hydrate.prototype.setErrorHandler=function(errorHandler){this.errorHandler=errorHandler;};Hydrate._refMatcher=/__hydrate_ref_(.*)/;Hydrate.prototype.parse=function(input){var l,o,obj,obj_key,ref_id,reference,_i,_len,_ref;this.identified_objects=[];this.references_to_resolve=[];o=JSON.parse(input);l=o.length;o=this.fixTree(o);_ref=this.references_to_resolve;for(_i=0,_len=_ref.length;_i<_len;_i++){reference=_ref[_i];obj=reference[0],obj_key=reference[1],ref_id=reference[2];obj[obj_key]=this.identified_objects[ref_id];}
|
||||
this.clean(o);return o;};Hydrate.prototype.fixTree=function(obj){var k,k2,m,proto,t,tmp,v,v2,_len;if(obj instanceof Array){for(k=0,_len=obj.length;k<_len;k++){v=obj[k];v=this.fixTree(v);if(typeof v==="string"&&(m=v.match(Hydrate._refMatcher))){k2=Util.h2d(m[1]);this.references_to_resolve.push([obj,k,k2]);}else{obj[k]=v;}}}else if(typeof obj==="object"){if(obj&&(obj.__hydrate_cons!=null)){proto=this.resolvePrototype(obj.__hydrate_cons);if(proto!=null){if(Util.supportsProto){obj.__proto__=proto;}else{tmp=(function(){});tmp.prototype=proto;t=new tmp;for(k in obj){v=obj[k];t[k]=v;}
|
||||
obj=t;}}else{this.errorHandler(new Hydrate.PrototypeNotFoundError(obj,obj.__hydrate_cons));}}
|
||||
for(k in obj){v=obj[k];v=this.fixTree(v);if(k==="__hydrate_id"){v2=Util.h2d(v);this.identified_objects[v2]=obj;}else if(v==="__hydrate_undef"){obj[k]=void 0;}else if(typeof v==="string"&&(m=v.match(Hydrate._refMatcher))){k2=Util.h2d(m[1]);this.references_to_resolve.push([obj,k,k2]);}else{obj[k]=v;}}}
|
||||
return obj;};Hydrate.prototype.resolvePrototype=function(cons_id){if(!(this.resolver!=null)){throw new Error("No Hydrate resolver found -- you should specify one in the Hydrate constructor!");}
|
||||
return this.resolver.resolve(cons_id);};Hydrate.prototype.clean=function(o,cleaned){var k,migrations,num,v,_ref,_ref2;if(cleaned==null){cleaned=[];}
|
||||
migrations=this.migrations[o.__hydrate_cons];if((o.version!=null)&&(migrations!=null)&&o.version<migrations.length){for(num=_ref=o.version,_ref2=migrations.length- 1;(_ref<=_ref2?num<=_ref2:num>=_ref2);(_ref<=_ref2?num+=1:num-=1)){migrations[num].call(o);}
|
||||
delete o.version;}
|
||||
cleaned.push(o);if(typeof o==="object"&&!(o instanceof Array)){for(k in o){v=o[k];if(k==="__hydrate_id"||k==="__hydrate_cons"){delete o[k];}else if(typeof v==="object"&&v&&!(o instanceof Array)&&cleaned.indexOf(v)<0){this.clean(v,cleaned);}}}
|
||||
return true;};Hydrate.prototype.migration=function(klass,index,callback){var all_versions;switch(typeof klass){case"function":klass=klass.name;if(klass===""){this.errorHandler(new AnonymousConstructorError(klass));}
|
||||
break;case"string":null;break;default:throw new Error("invalid class passed in; pass a function or a string");}
|
||||
all_versions=this.migrations[klass];if(!(all_versions!=null)){all_versions=this.migrations[klass]=[];}
|
||||
all_versions[index- 1]=callback;return true;};return Hydrate;}).call(this);Resolver=(function(){function Resolver(){}
|
||||
Resolver.prototype.resolve=function(cons_id){throw new Error("abstract");};return Resolver;})();ContextResolver=(function(){__extends(ContextResolver,Resolver);function ContextResolver(context){this.context=context;}
|
||||
ContextResolver.prototype.resolve=function(cons_id){var v;v=this.context[cons_id];if(v!=null){return v.prototype;}else{return null;}};return ContextResolver;})();MultiResolver=(function(){__extends(MultiResolver,Resolver);function MultiResolver(resolvers){this.resolvers=resolvers!=null?resolvers:[];}
|
||||
MultiResolver.prototype.resolve=function(cons_id){var proto,res,_i,_len,_ref;_ref=this.resolvers;for(_i=0,_len=_ref.length;_i<_len;_i++){res=_ref[_i];proto=res.resolve(cons_id);if(proto!=null){return proto;}}
|
||||
return null;};return MultiResolver;})();Hydrate.Resolver=Resolver;Hydrate.ContextResolver=ContextResolver;Hydrate.MultiResolver=MultiResolver;this.Hydrate=Hydrate;}).call(this);
|
||||
742
test/jasmine-standalone-1.3.1/src/OfflineStore.js
Executable file
742
test/jasmine-standalone-1.3.1/src/OfflineStore.js
Executable file
@ -0,0 +1,742 @@
|
||||
/**
|
||||
* Offline library for storing graphics related to an Edit Task.
|
||||
* Currently works with Points, Polylines and Polygons. Also provides online/offline validation.
|
||||
*
|
||||
* Automatically attempts to reconnect. As soon as a connection is made updates are submitted
|
||||
* and the localStorage is deleted upon successful update.
|
||||
*
|
||||
* NOTE: Hooks for listeners that updates were successful/unsuccessful should be added in
|
||||
* _handleRestablishedInternet()
|
||||
*
|
||||
* <b>Dependencies:</b> ArcGIS JavaScript API and Hydrate.js: https://github.com/nanodeath/HydrateJS
|
||||
* <b>Limitations:</b> does not currently store infoTemplate and symbol properties
|
||||
* <b>More info:</b> http://www.w3.org/TR/webstorage/
|
||||
* @version 0.1
|
||||
* @author Andy Gup (@agup)
|
||||
* @param layersAddResult the layers-add-result. Example: map.on("layers-add-result", someFunction);
|
||||
* @type {*|{}}
|
||||
*/
|
||||
var OfflineStore = function(/* Map */ map) {
|
||||
|
||||
this.backgroundTimerWorker = null;
|
||||
this.isTimer = null;
|
||||
this.layers = []; //An array of all feature layers
|
||||
this.map = map;
|
||||
if(map != null) {
|
||||
this.map.offlineStore = this
|
||||
}
|
||||
else{
|
||||
console.log("map is null")
|
||||
throw("map is null");
|
||||
}
|
||||
|
||||
/**
|
||||
* Public ENUMs (Constants)
|
||||
* @type {Object}
|
||||
* @returns {String}
|
||||
* @private
|
||||
*/
|
||||
this.enum = (function(){
|
||||
var values = {
|
||||
ADD : "add",
|
||||
UPDATE : "update",
|
||||
DELETE : "delete"
|
||||
}
|
||||
|
||||
return values;
|
||||
});
|
||||
|
||||
/**
|
||||
* Private Local ENUMs (Constants)
|
||||
* Contains required configuration info.
|
||||
* @type {Object}
|
||||
* @returns {*}
|
||||
* @private
|
||||
*/
|
||||
this._localEnum = (function(){
|
||||
var values = {
|
||||
VALIDATION_URL : "http://localhost/offline/test.html", /* Change this to a remote server for testing! */
|
||||
TIMER_URL : "./src/Timer.js", /* For use within a child process only */
|
||||
STORAGE_KEY : "___EsriOfflineStore___", /* Unique key for setting/retrieving values from localStorage */
|
||||
INDEX_KEY : "___EsriOfflineIndex___", /* Index for tracking each action (add, delete, update) in local store */
|
||||
VALIDATION_TIMEOUT : 10 * 1000, /* HTTP timeout when trying to validate internet on/off */
|
||||
LOCAL_STORAGE_MAX_LIMIT : 4.75 /* MB */, /* Most browsers offer default storage of ~5MB */
|
||||
TOKEN : "|||", /* A unique token for tokenizing stringified localStorage values */
|
||||
REQUIRED_LIBS : [
|
||||
"./src/Hydrate.js",
|
||||
"./src/Poller.js"
|
||||
]
|
||||
}
|
||||
|
||||
return values;
|
||||
});
|
||||
|
||||
/**
|
||||
* Model for handle vertices editing
|
||||
* @param graphic
|
||||
* @param layer
|
||||
*/
|
||||
this.verticesObject = function(/* Graphic */ graphic, /* FeatureLayer */ layer){
|
||||
this.graphic = graphic;
|
||||
this.layer = layer;
|
||||
}
|
||||
|
||||
//////////////////////////
|
||||
///
|
||||
/// PUBLIC methods
|
||||
///
|
||||
//////////////////////////
|
||||
|
||||
/**
|
||||
* Conditionally attempts to send an edit request to ArcGIS Server.
|
||||
* @param graphic Required
|
||||
* @param layer Required
|
||||
* @param enumValue Required
|
||||
*/
|
||||
this.applyEdits = function(/* Graphic */ graphic,/* FeatureLayer */ layer, /* String */ enumValue){
|
||||
|
||||
var internet = this._checkInternet();
|
||||
|
||||
//TODO Need to add code to determine size of incoming graphic
|
||||
var mb = this.getlocalStorageUsed();
|
||||
console.log("getlocalStorageUsed = " + mb + " MBs");
|
||||
|
||||
if(mb > this._localEnum().LOCAL_STORAGE_MAX_LIMIT /* MB */){
|
||||
alert("You are almost over the local storage limit. No more data can be added.")
|
||||
return;
|
||||
}
|
||||
|
||||
if(internet === false){
|
||||
this._addToLocalStore(graphic,layer,enumValue);
|
||||
if(this.isTimer == null){
|
||||
this._startTimer(function(err){
|
||||
throw ("unable to start background timer. Offline edits won't work. " + err.stack);
|
||||
});
|
||||
}
|
||||
}
|
||||
else if(internet == null || typeof internet == "undefined"){
|
||||
console.log("applyEdits: possible error.");
|
||||
}
|
||||
else{
|
||||
this._layerEditManager(graphic,layer,enumValue,this.enum(),null,true,null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Public method for retrieving all items in the localStore.
|
||||
* @returns {Array} Graphics
|
||||
*/
|
||||
this.getStore = function(){
|
||||
var graphicsArr = null;
|
||||
var data = localStorage.getItem(this._localEnum().STORAGE_KEY);
|
||||
|
||||
if(data != null){
|
||||
graphicsArr = [];
|
||||
var split = data.split(this._localEnum().TOKEN);
|
||||
for(var property in split){
|
||||
|
||||
var item = split[property];
|
||||
|
||||
if(typeof item !== "undefined" && item.length > 0 && item !== null){
|
||||
var graphic = this._deserializeGraphic(item);
|
||||
graphicsArr.push( graphic );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return graphicsArr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a list of all localStorage items that have been either
|
||||
* added, deleted or updated.
|
||||
* @returns {Array}
|
||||
*/
|
||||
this.getLocalStoreIndex = function(){
|
||||
var localStore = localStorage.getItem(this._localEnum().INDEX_KEY);
|
||||
return localStore != null ? localStore.split(this._localEnum().TOKEN) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines total storage used for this domain.
|
||||
* @returns Number MB's
|
||||
*/
|
||||
this.getlocalStorageUsed = function(){
|
||||
var mb = 0;
|
||||
|
||||
//IE hack
|
||||
if(window.localStorage.hasOwnProperty("remainingspace")){
|
||||
//http://msdn.microsoft.com/en-us/library/ie/cc197016(v=vs.85).aspx
|
||||
mb = window.localStorage.remainingSpace/1024/1024;
|
||||
}
|
||||
else{
|
||||
for(var x in localStorage){
|
||||
//Uncomment out console.log to see *all* items in local storage
|
||||
//console.log(x+"="+((localStorage[x].length * 2)/1024/1024).toFixed(2)+" MB");
|
||||
mb += localStorage[x].length
|
||||
}
|
||||
}
|
||||
|
||||
return Math.round(((mb * 2)/1024/1024) * 100)/100;
|
||||
}
|
||||
|
||||
//////////////////////////
|
||||
///
|
||||
/// PRIVATE methods
|
||||
///
|
||||
//////////////////////////
|
||||
|
||||
this._layerEditManager = function(
|
||||
/* Graphic */ graphic,
|
||||
/* FeatureLayer */ layer,
|
||||
/* String */ value,
|
||||
/* Object */ localEnum,
|
||||
/* Number */ count,
|
||||
/* Object */ mCallback){
|
||||
|
||||
switch(value){
|
||||
case localEnum.DELETE:
|
||||
layer.applyEdits(null,null,[graphic],function(addResult,updateResult,deleteResult){
|
||||
console.log("deleteResult ObjectId: " + deleteResult[0].objectId + ", Success: " + deleteResult[0].success);
|
||||
if(mCallback != null && count != null) {
|
||||
mCallback(count,deleteResult[0].success);
|
||||
}
|
||||
else{
|
||||
this._addItemLocalStoreIndex(deleteResult[0].objectId,value,true);
|
||||
}
|
||||
|
||||
}.bind(this),
|
||||
function(error){
|
||||
console.log("_layer: " + error.stack); mCallback(count,false);
|
||||
this._addItemLocalStoreIndex(deleteResult[0].objectId,value,false);
|
||||
}.bind(this)
|
||||
);
|
||||
break;
|
||||
case localEnum.ADD:
|
||||
layer.applyEdits([graphic],null,null,function(addResult,updateResult,deleteResult){
|
||||
console.log("addResult ObjectId: " + addResult[0].objectId + ", Success: " + addResult[0].success);
|
||||
if(mCallback != null && count != null) {
|
||||
mCallback(count,deleteResult[0].success);
|
||||
}
|
||||
else{
|
||||
this._addItemLocalStoreIndex(addResult[0].objectId,value,true);
|
||||
}
|
||||
}.bind(this),
|
||||
function(error){
|
||||
console.log("_layer: " + error.stack); mCallback(count,false);
|
||||
this._addItemLocalStoreIndex(addResult[0].objectId,value,false);
|
||||
}.bind(this)
|
||||
);
|
||||
break;
|
||||
case localEnum.UPDATE:
|
||||
layer.applyEdits(null,[graphic],null,function(addResult,updateResult,deleteResult){
|
||||
console.log("updateResult ObjectId: " + updateResult[0].objectId + ", Success: " + updateResult[0].success);
|
||||
if(mCallback != null && count != null) {
|
||||
mCallback(count,deleteResult[0].success);
|
||||
}
|
||||
else{
|
||||
this._addItemLocalStoreIndex(updateResult[0].objectId,value,true);
|
||||
}
|
||||
}.bind(this),
|
||||
function(error){
|
||||
console.log("_layer: " + error.stack); mCallback(count,false)
|
||||
this._addItemLocalStoreIndex(updateResult[0].objectId,value,false);
|
||||
}.bind(this)
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this._layerCallbackHandler = function(callback,count,objectid){
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a serialized geometry and adds it to localStorage
|
||||
* @param geom
|
||||
* @private
|
||||
*/
|
||||
this._updateExistingLocalStore = function(/* Geometry */ geom){
|
||||
|
||||
var localStore = this._getLocalStorage();
|
||||
var split = localStore.split(this._localEnum().TOKEN);
|
||||
console.log(localStore.toString());
|
||||
var dupeFlag = false;
|
||||
for(var property in split){
|
||||
var item = split[property];
|
||||
if(typeof item !== "undefined" && item.length > 0 && item !== null){
|
||||
var sub = geom.substring(0,geom.length - 3);
|
||||
|
||||
//This is not the sturdiest way to verify if two geometries are equal
|
||||
if(sub === item){
|
||||
console.log("updateExistingLocalStore: duplicate item skipped.");
|
||||
dupeFlag = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(dupeFlag == false) this._setItemInLocalStore(localStore + geom);
|
||||
}
|
||||
|
||||
this._addToLocalStore = function(/* Graphic */ graphic, /* FeatureLayer */ layer, /* String */ enumValue){
|
||||
var arr = this._getLocalStorage();
|
||||
var geom = this._serializeGraphic(graphic,layer,enumValue);
|
||||
|
||||
//If localStorage does NOT exist
|
||||
if(arr === null){
|
||||
|
||||
this._setItemInLocalStore(geom);
|
||||
}
|
||||
else{
|
||||
this._updateExistingLocalStore(geom);
|
||||
}
|
||||
|
||||
layer.add(graphic);
|
||||
}
|
||||
|
||||
this._startTimer = function(callback){
|
||||
|
||||
var onlineFLAG = false;
|
||||
|
||||
if(this.backgroundTimerWorker == null && this.isTimer == null){
|
||||
|
||||
console.log("Starting timer...");
|
||||
|
||||
try{
|
||||
this.backgroundTimerWorker = new Worker(this._localEnum().TIMER_URL);
|
||||
this.backgroundTimerWorker.addEventListener('message', function(msg) {
|
||||
|
||||
if(msg.data.hasOwnProperty("msg")){
|
||||
console.log("_startTimer: " + msg.data.msg)
|
||||
}
|
||||
if(msg.data.hasOwnProperty("alive")){
|
||||
console.log("Timer heartbeat.");
|
||||
this.isTimer = msg.data.alive;
|
||||
}
|
||||
if(msg.data.hasOwnProperty("err")){
|
||||
console.log("_startTimer error: " + msg.data.err);
|
||||
}
|
||||
|
||||
//Handle reestablishing an internet connection
|
||||
if(msg.data.hasOwnProperty("net")){
|
||||
if(msg.data.net == false){
|
||||
console.log("Internet status: " + msg.data.net);
|
||||
if(onlineFLAG != false)onlineFLAG = false;
|
||||
}
|
||||
else if(msg.data.net == true){
|
||||
var arr = this._getLocalStorage();
|
||||
if(onlineFLAG == false){
|
||||
onlineFLAG = true;
|
||||
}
|
||||
if(arr != null){
|
||||
this._handleRestablishedInternet(function(){
|
||||
this._stopTimer();
|
||||
this._deleteStore();
|
||||
}.bind(this));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}.bind(this), false);
|
||||
this.backgroundTimerWorker.postMessage({start:true,interval:10000});
|
||||
}
|
||||
catch(err){
|
||||
callback(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
this._stopTimer = function(){
|
||||
|
||||
if(this.backgroundTimerWorker != null){
|
||||
this.backgroundTimerWorker.terminate();
|
||||
this.backgroundTimerWorker.postMessage({kill:true});
|
||||
this.backgroundTimerWorker = null;
|
||||
this.isTimer = null;
|
||||
console.log("Timer stopped...")
|
||||
}
|
||||
else{
|
||||
console.log("Timer may already be stopped...");
|
||||
}
|
||||
}
|
||||
|
||||
this._handleRestablishedInternet = function(callback){
|
||||
var graphicsArr = this.getStore();
|
||||
|
||||
if(graphicsArr != null && this.layers != null){
|
||||
|
||||
var check = [];
|
||||
var errCnt = 0;
|
||||
for(var i in graphicsArr){
|
||||
var obj1 = graphicsArr[i];
|
||||
var layer = this._getGraphicsLayerById(obj1.layer);
|
||||
this._layerEditManager(obj1.graphic,layer,obj1.enumValue,this.enum(),i,function(/* Number */ num, /* boolean */ success){
|
||||
check.push(num);
|
||||
|
||||
var id = obj1.graphic.attributes.objectid;
|
||||
|
||||
if(success == true && check.length == graphicsArr.length){
|
||||
if(errCnt == 0){
|
||||
this._addItemLocalStoreIndex(id,obj1.enumValue,true);
|
||||
callback();
|
||||
}
|
||||
else{
|
||||
console.log("_handleRestablishedInternet: there were errors. LocalStore still available.");
|
||||
this._stopTimer();
|
||||
}
|
||||
}
|
||||
else if(success == true && check.length < graphicsArr.length){
|
||||
this._addItemLocalStoreIndex(id,obj1.enumValue,true);
|
||||
}
|
||||
else if(success == false && check.length == graphicsArr.length){
|
||||
this._addItemLocalStoreIndex(id,obj1.enumValue,false);
|
||||
console.log("_handleRestablishedInternet: error sending edit on " + id);
|
||||
this._stopTimer();
|
||||
}
|
||||
else if(success == false && check.length < graphicsArr.length){
|
||||
this._addItemLocalStoreIndex(id,obj1.enumValue,false);
|
||||
errCnt++;
|
||||
console.log("_handleRestablishedInternet: error sending edit on " + id);
|
||||
}
|
||||
}.bind(this));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this._getGraphicsLayerById = function(/* String */ id){
|
||||
for(var layer in this.layers)
|
||||
{
|
||||
if(id == this.layers[layer].layerId){
|
||||
return this.layers[layer];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all items stored by this library using its unique key.
|
||||
* Does NOT delete anything else from localStorage.
|
||||
*/
|
||||
this._deleteStore = function(){
|
||||
console.log("deleting localStore");
|
||||
try{
|
||||
localStorage.removeItem(this._localEnum().STORAGE_KEY);
|
||||
}
|
||||
catch(err){
|
||||
return err.stack;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the raw local storage object.
|
||||
* @returns {*}
|
||||
* @private
|
||||
*/
|
||||
this._getLocalStorage = function(){
|
||||
return localStorage.getItem(this._localEnum().STORAGE_KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the localStorage
|
||||
* @param item
|
||||
* @returns {boolean} returns true if success, else false. Writes
|
||||
* error stack to console.
|
||||
*/
|
||||
this._setItemInLocalStore = function(item){
|
||||
var success = false;
|
||||
|
||||
try{
|
||||
localStorage.setItem(this._localEnum().STORAGE_KEY,item);
|
||||
success = true;
|
||||
}
|
||||
catch(err){
|
||||
console.log("_setItemInLocalStore(): " + err.stack);
|
||||
success = false;
|
||||
}
|
||||
|
||||
return success;
|
||||
|
||||
}
|
||||
|
||||
this._deleteLocalStoreIndex = function(){
|
||||
console.log("deleting localStoreIndex");
|
||||
try{
|
||||
localStorage.removeItem(this._localEnum().INDEX_KEY);
|
||||
}
|
||||
catch(err){
|
||||
return err.stack;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates if an item has been deleted.
|
||||
* @param objectId
|
||||
* @returns {boolean}
|
||||
* @private
|
||||
*/
|
||||
this._getItemLocalStoreIndex = function(/* String */ objectId){
|
||||
var localStore = this._getLocalStorageIndex();
|
||||
var split = localStore.split(this._localEnum().TOKEN);
|
||||
for(var property in split){
|
||||
var item = JSON.parse(split[property]);
|
||||
if(typeof item !== "undefined" || item.length > 0 || item != null){
|
||||
if(item.hasOwnProperty("id") && item.id == objectId){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add item to index *if* if was successfully deleted.
|
||||
* @param objectId
|
||||
* @param type enum
|
||||
* @param success
|
||||
* @returns {boolean}
|
||||
* @private
|
||||
*/
|
||||
this._addItemLocalStoreIndex = function(/* String */ objectId, /* String */ type, /* boolean */ success){
|
||||
var index = new this._indexObject(objectId,type,success) ;
|
||||
var mIndex = JSON.stringify(index);
|
||||
|
||||
var localStore = this.getLocalStoreIndex();
|
||||
|
||||
try{
|
||||
if(localStore == null || typeof localStore == "undefined"){
|
||||
localStorage.setItem(this._localEnum().INDEX_KEY,mIndex + this._localEnum().TOKEN);
|
||||
}
|
||||
else{
|
||||
localStorage.setItem(this._localEnum().INDEX_KEY,localStore + mIndex + this._localEnum().TOKEN);
|
||||
}
|
||||
|
||||
success = true;
|
||||
}
|
||||
catch(err){
|
||||
console.log("_addItemLocalStoreIndex(): " + err.stack);
|
||||
success = false;
|
||||
}
|
||||
|
||||
return success;
|
||||
|
||||
}
|
||||
|
||||
this._checkInternet = function(){
|
||||
var result = null;
|
||||
|
||||
var poller = Poller.httpGet(
|
||||
this._localEnum().VALIDATION_URL,
|
||||
this._localEnum().VALIDATION_TIMEOUT,
|
||||
function(msg){
|
||||
result = msg;
|
||||
}
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
this._deserializeGraphic = function(/* Graphic */ item){
|
||||
|
||||
var jsonItem = JSON.parse(item);
|
||||
var geometry = JSON.parse(jsonItem.geometry);
|
||||
var attributes = JSON.parse(jsonItem.attributes);
|
||||
var enumValue = jsonItem.enumValue;
|
||||
var layer = JSON.parse(jsonItem.layer);
|
||||
var finalGeom = null;
|
||||
|
||||
switch(geometry.type){
|
||||
case "polyline":
|
||||
finalGeom = new esri.geometry.Polyline(new esri.SpatialReference(geometry.spatialReference.wkid));
|
||||
for(var path in geometry.paths){
|
||||
finalGeom.addPath(geometry.paths[path]);
|
||||
}
|
||||
break
|
||||
case "point":
|
||||
finalGeom = new esri.geometry.Point(geometry.x,geometry.y,new esri.SpatialReference(geometry.spatialReference.wkid));
|
||||
break;
|
||||
case "polygon":
|
||||
finalGeom = new esri.geometry.Polygon(new esri.SpatialReference(geometry.spatialReference.wkid));
|
||||
for(var ring in geometry.rings){
|
||||
finalGeom.addRing(geometry.rings[ring]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
var graphic = new esri.Graphic(finalGeom, null, attributes, null);
|
||||
|
||||
return {"graphic":graphic,"layer":layer,"enumValue":enumValue};
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebuilds Geometry in a way that can be serialized/deserialized
|
||||
* @param Graphic
|
||||
* @returns {string}
|
||||
* @private
|
||||
*/
|
||||
this._serializeGraphic = function(/* Graphic */ object, layer, enumValue){
|
||||
var json = new this._jsonObject();
|
||||
json.layer = layer.layerId;
|
||||
json.enumValue = enumValue;
|
||||
json.geometry = JSON.stringify(object.geometry)
|
||||
|
||||
if(object.hasOwnProperty("attributes")){
|
||||
if(object.attributes != null){
|
||||
var hydrate = new Hydrate();
|
||||
var q = hydrate.stringify(object.attributes);
|
||||
json.attributes = q;
|
||||
}
|
||||
}
|
||||
|
||||
return JSON.stringify(json) + this._localEnum().TOKEN;
|
||||
}
|
||||
|
||||
//////////////////////////
|
||||
///
|
||||
/// INTERNAL Models
|
||||
///
|
||||
//////////////////////////
|
||||
|
||||
/**
|
||||
* Model for storing serialized graphics
|
||||
* @private
|
||||
*/
|
||||
this._jsonObject = function(){
|
||||
this.layer = null;
|
||||
this.enumValue = null;
|
||||
this.geometry = null;
|
||||
this.attributes = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Model for storing serialized index info.
|
||||
* @private
|
||||
*/
|
||||
this._indexObject = function(/* String */ id, /* String */ type, /* boolean */ success){
|
||||
this.id = id;
|
||||
this.type = type;
|
||||
this.success = success;
|
||||
}
|
||||
|
||||
//////////////////////////
|
||||
///
|
||||
/// INITIALISE
|
||||
///
|
||||
//////////////////////////
|
||||
|
||||
/**
|
||||
* Load src
|
||||
* TO-DO: Needs to be made AMD compliant!
|
||||
* @param urlArray
|
||||
* @param callback
|
||||
* @private
|
||||
*/
|
||||
this._loadScripts = function(/* Array */ urlArray, callback)
|
||||
{
|
||||
count = 0;
|
||||
for(var i in urlArray){
|
||||
try{
|
||||
var head = document.getElementsByTagName('head')[0];
|
||||
var script = document.createElement('script');
|
||||
script.type = 'text/javascript';
|
||||
script.src = urlArray[i];
|
||||
script.onreadystatechange = function(){
|
||||
count++;
|
||||
console.log("Script loaded. " + this.src);
|
||||
if(count == urlArray.length) callback();
|
||||
};
|
||||
script.onload = function(){
|
||||
count++;
|
||||
console.log("Script loaded. " + this.src);
|
||||
if(count == urlArray.length) callback();
|
||||
};
|
||||
head.appendChild(script);
|
||||
}
|
||||
catch(err){
|
||||
console.log("_loadScripts: " + err.stack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this._parseFeatureLayers = function(/* Event */ map){
|
||||
|
||||
var layerIds = map.graphicsLayerIds;
|
||||
|
||||
try{
|
||||
for (var i in layerIds){
|
||||
var layer = map.getLayer(layerIds[i]);
|
||||
|
||||
if(layer.hasOwnProperty("type") && layer.type.toLowerCase() == "feature layer"){
|
||||
if(layer.isEditable() == true){
|
||||
this.layers.push(layer);
|
||||
}
|
||||
}
|
||||
else{
|
||||
throw ("Layer not editable: " + layer.url );
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(err){
|
||||
console.log("_parseFeatureLayer: " + err.stack);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the OfflineStore library. Loads required src. Kicks off timer if
|
||||
* localStore is not empty.
|
||||
* @see Required script sare set in _localEnum.
|
||||
* @type {*}
|
||||
* @private
|
||||
*/
|
||||
this._init = function(){
|
||||
this._loadScripts(this._localEnum().REQUIRED_LIBS,function(){
|
||||
console.log("OfflineStore is ready.")
|
||||
|
||||
this._parseFeatureLayers(this.map);
|
||||
|
||||
if(typeof Poller == "object"){
|
||||
var internet = this._checkInternet();
|
||||
var arr = this._getLocalStorage();
|
||||
|
||||
if(this.isTimer != true && internet == false && arr != null){
|
||||
this._startTimer(function(err){
|
||||
throw ("unable to start background timer. Offline edits won't work. " + err.stack);
|
||||
});
|
||||
}
|
||||
else if(internet == null || typeof internet == "undefined"){
|
||||
console.log("applyEdits: possible error.");
|
||||
}
|
||||
// else{
|
||||
// var arr = this._getLocalStorage();
|
||||
// if(arr != null){
|
||||
// this._handleRestablishedInternet(function(){
|
||||
// this._stopTimer();
|
||||
// this._deleteStore();
|
||||
// }.bind(this));
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
}.bind(this));
|
||||
}.bind(this)()
|
||||
|
||||
/**
|
||||
* Attempt to stop timer and reduce chances of corrupting or duplicating data.
|
||||
* TO-DO some errors like those in callbacks may not be trapped by this!
|
||||
* @param msg
|
||||
* @param url
|
||||
* @param line
|
||||
* @returns {boolean}
|
||||
*/
|
||||
window.onerror = function (msg,url,line){
|
||||
console.log(msg + ", " + url + ":" + line);
|
||||
this.map.offlineStore._stopTimer();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
68
test/jasmine-standalone-1.3.1/src/Poller.js
Executable file
68
test/jasmine-standalone-1.3.1/src/Poller.js
Executable file
@ -0,0 +1,68 @@
|
||||
/**
|
||||
* Poller is a static library for sending data via HTTP requests. You can use it directly
|
||||
* without needing to instantiate it via "new".
|
||||
* @type {*|{}}
|
||||
*/
|
||||
var Poller = Poller || {};
|
||||
|
||||
/**
|
||||
* Static method that validates if application is online. Provides full request validation
|
||||
* on the response payload. You can either use this directly as a wrapper on an HTTP REST
|
||||
* request or as a separate validation check.
|
||||
* Reference: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest
|
||||
* @param url
|
||||
* @param timeout http timeout in milliseconds (ms)
|
||||
* @param callback returns boolean {response:true/false}
|
||||
*/
|
||||
Poller.httpGet = function(url,timeout,callback)
|
||||
{
|
||||
if(navigator.onLine == true){
|
||||
callback(true);
|
||||
}
|
||||
else if(navigator.onLine == false){
|
||||
callback(false);
|
||||
}
|
||||
else{
|
||||
Poller._makeRequest(url,timeout,callback);
|
||||
}
|
||||
}
|
||||
|
||||
Poller._makeRequest = function(url,timeout,callback){
|
||||
try{
|
||||
var xmlHttp = new XMLHttpRequest();
|
||||
xmlHttp.callback = callback;
|
||||
xmlHttp.onload = Poller._handleRequest;
|
||||
xmlHttp.onerror = Poller._error;
|
||||
xmlHttp.open( "GET", url, true );
|
||||
xmlHttp.timeout = timeout;
|
||||
xmlHttp.ontimeout = Poller._handleTimeout;
|
||||
xmlHttp.send();
|
||||
}
|
||||
catch(err){
|
||||
this.callback(false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Poller._error = function(){
|
||||
this.callback( false );
|
||||
}
|
||||
|
||||
Poller._handleRequest = function(){
|
||||
|
||||
if ( this != null && this.readyState === 4)
|
||||
{
|
||||
if(this.status === 200){
|
||||
this.callback( true );
|
||||
}
|
||||
else{
|
||||
this.callback(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Poller._handleTimeout = function(){
|
||||
if (this != null){
|
||||
this.callback(false);
|
||||
}
|
||||
}
|
||||
88
test/jasmine-standalone-1.3.1/src/Timer.js
Executable file
88
test/jasmine-standalone-1.3.1/src/Timer.js
Executable file
@ -0,0 +1,88 @@
|
||||
/**
|
||||
* Timer.js is designed as a centralized timer library to handle timing
|
||||
* for an entire application.
|
||||
*
|
||||
* This version of the library polls the internet and sends messages back
|
||||
* to the parent application is internet is enabled or not.
|
||||
*
|
||||
* NOTE: Run this library inside a webworker process.
|
||||
*/
|
||||
importScripts('Poller.js');
|
||||
|
||||
var ___backgroundTimer = null;
|
||||
|
||||
/**
|
||||
* Local ENUMs (Constants)
|
||||
* @type {Object}
|
||||
* @returns {*}
|
||||
*/
|
||||
this._localEnum = (function(){
|
||||
var values = {
|
||||
VALIDATION_URL : "http://localhost/test/test.html",
|
||||
VALIDATION_TIMEOUT : 10 * 1000
|
||||
}
|
||||
|
||||
return values;
|
||||
});
|
||||
|
||||
this._checkInternet = function(callback){
|
||||
var result = null;
|
||||
Poller.httpGet(
|
||||
this._localEnum().VALIDATION_URL,
|
||||
this._localEnum().VALIDATION_TIMEOUT,
|
||||
function(msg){
|
||||
callback( msg );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
this._startTimer = function(msg){
|
||||
var count = 0;
|
||||
|
||||
___backgroundTimer = setInterval(function(){
|
||||
|
||||
try{
|
||||
var date = new Date();
|
||||
self.postMessage({msg:"Timer.js: datetime tick: " + date.toUTCString()});
|
||||
self.postMessage({alive:true});
|
||||
this._checkInternet(function(evt){
|
||||
self.postMessage({net:evt});
|
||||
})
|
||||
}
|
||||
catch(err){
|
||||
count++;
|
||||
if(count == 3){
|
||||
self.postMessage({err:"Timer.js: shutdown timer...too many errors. " + err.message});
|
||||
clearInterval(___backgroundTimer);
|
||||
self.postMessage({alive:false});
|
||||
}
|
||||
else{
|
||||
self.postMessage({err:"Timer.js: " + err.message + "\n" + err.stack});
|
||||
self.postMessage({alive:false});
|
||||
}
|
||||
}
|
||||
}.bind(this),msg.interval);
|
||||
}
|
||||
|
||||
self.addEventListener('message', function(msg) {
|
||||
|
||||
if(msg.data.hasOwnProperty("start") && msg.data.hasOwnProperty("interval")){
|
||||
if(msg.data.start == true && ___backgroundTimer == null){
|
||||
self.postMessage({msg:"Timer: start message recv'd"});
|
||||
this._startTimer(msg.data);
|
||||
}
|
||||
}
|
||||
if(msg.data.hasOwnProperty("running")){
|
||||
if(___backgroundTimer){
|
||||
self.postMessage({alive:true});
|
||||
}
|
||||
else{
|
||||
self.postMessage({alive:false});
|
||||
}
|
||||
}
|
||||
if(msg.data.hasOwnProperty("kill")){
|
||||
clearInterval(___backgroundTimer);
|
||||
self.postMessage({alive:false});
|
||||
}
|
||||
|
||||
}.bind(this), false);
|
||||
1
test/jasmine-standalone-1.3.1/test.html
Normal file
1
test/jasmine-standalone-1.3.1/test.html
Normal file
@ -0,0 +1 @@
|
||||
{response:true}
|
||||
Loading…
x
Reference in New Issue
Block a user