update from RIFT as of 696b75d2fe9fb046261b08c616f1bcf6c0b54a9b third try

Signed-off-by: Jeremy Mordkoff <Jeremy.Mordkoff@riftio.com>

Change-Id: Ib11aa03a2eff5a53c808342508a5d7bee7b202b8
diff --git a/skyquake/framework/source/SourceCache.js b/skyquake/framework/source/SourceCache.js
new file mode 100644
index 0000000..ded3c9d
--- /dev/null
+++ b/skyquake/framework/source/SourceCache.js
@@ -0,0 +1,96 @@
+/*
+ * 
+ *   Copyright 2017 RIFT.IO Inc
+ *
+ *   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.
+ *
+ */
+import appConfiguration from '../utils/appConfiguration'
+
+let versionKeyPrefix = null;
+
+const localCache = new class {
+    get(key) {
+        let valueAsString = localStorage.getItem(key);
+        return valueAsString ? JSON.parse(valueAsString) : undefined;
+    }
+    set(key, val) {
+        localStorage.setItem(key, typeof val === 'string' ? val : JSON.stringify(val));
+    }
+}();
+
+let objCache = new Map();
+
+const storeCache = new class {
+    get(key) {
+        if (objCache[key]) {
+            objCache[key].timerId && clearTimeout(objCache[key].timerId)
+            objCache[key].timerId = setTimeout((key) => delete objCache[key], 2000)
+            return objCache[key].value;
+        } 
+        const obj = localCache.get(key);
+        if (obj) {
+            objCache[key] = {
+                value: obj,
+                timerId: setTimeout((key) => delete objCache[key], 2000)
+            }
+            return obj;
+        }
+    }
+    set(key, obj) {
+        setTimeout(localCache.set, 100, key, obj);
+        objCache[key] = {
+            value: obj,
+            timerId: setTimeout((key) => delete objCache[key], 2000)
+        }
+    }
+    init(version) {
+        versionKeyPrefix = 's-v-' + version;
+        const currentStoreVersion = localStorage.getItem('store-version');
+        if (currentStoreVersion !== version) {
+            let removeItems = [];
+            for (let i = 0; i < localStorage.length; ++i) {
+                let key = localStorage.key(i);
+                if (key.startsWith('s-v-')) {
+                    removeItems.push(key);
+                }
+            }
+            removeItems.forEach((key) => localStorage.removeItem(key));
+            localStorage.setItem('store-version', version);
+        }
+    }
+}();
+
+class StoreCache {
+    constructor(name) {
+        this.name = 's-v-' + name;
+    }
+    get(key) {
+        return storeCache.get(this.name + key);
+    }
+    set(key, obj) {
+        storeCache.set(this.name + key, obj);
+    }
+    init() {
+        return versionKeyPrefix ? Promise.resolve() : new Promise(
+            (resolve, reject) => {
+                appConfiguration.get().then((config) => {
+                    storeCache.init(config.version);
+                    resolve();
+                })
+            }
+        )
+    }
+}
+
+module.exports = StoreCache;
\ No newline at end of file
diff --git a/skyquake/framework/source/model/index.js b/skyquake/framework/source/model/index.js
new file mode 100644
index 0000000..019ada0
--- /dev/null
+++ b/skyquake/framework/source/model/index.js
@@ -0,0 +1,7 @@
+import modelActions from './modelActions'
+import modelSource from './modelSource'
+
+export {
+    modelSource,
+    modelActions
+}
\ No newline at end of file
diff --git a/skyquake/framework/source/model/modelActions.js b/skyquake/framework/source/model/modelActions.js
new file mode 100644
index 0000000..41dbc00
--- /dev/null
+++ b/skyquake/framework/source/model/modelActions.js
@@ -0,0 +1,31 @@
+/*
+ * 
+ *   Copyright 2017 RIFT.IO Inc
+ *
+ *   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.
+ *
+ */
+import Alt from 'widgets/skyquake_container/skyquakeAltInstance';
+
+class Actions {
+
+	constructor() {
+		this.generateActions(
+            'loadModel',
+            'processRequestSuccess',
+            'processRequestInitiated',
+            'processRequestFailure');
+	}
+}
+
+export default Alt.createActions(Actions);
diff --git a/skyquake/framework/source/model/modelSource.js b/skyquake/framework/source/model/modelSource.js
new file mode 100644
index 0000000..3779aa5
--- /dev/null
+++ b/skyquake/framework/source/model/modelSource.js
@@ -0,0 +1,128 @@
+/*
+ * 
+ *   Copyright 2017 RIFT.IO Inc
+ *
+ *   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.
+ *
+ */
+import rw from 'utils/rw'
+import modelActions from './modelActions'
+import Utils from 'utils/utils'
+import $ from 'jquery';
+
+const source = {
+    loadModel: function () {
+        return {
+            remote: function (state, path) {
+                return new Promise(function (resolve, reject) {
+                    $.ajax({
+                        url: '/model?path=' + path,
+                        type: 'GET',
+                        success: function (result) {
+                            resolve(result);
+                        },
+                        error: function (xhr, errorType, errorMessage) {
+                            console.log("There was an error updating the model: ", errorType, errorMessage, xhr);
+                            reject({response: xhr.responseJSON, errorType, errorMessage});
+                        }
+                    });
+                })
+            },
+            success: modelActions.processRequestSuccess,
+            loading: modelActions.processRequestInitiated,
+            error: modelActions.processRequestFailure
+        }
+    },
+    updateModel: function () {
+        return {
+            remote: function (state, path, data) {
+                const url = path.reduce((url, node) => {
+                    url += node[0] !== '[' ? '/' : '';
+                    return url + node
+                }, `/model?path=/${state.path}`);                
+                return new Promise(function (resolve, reject) {
+                    $.ajax({
+                        url: url,
+                        type: 'PATCH',
+                        data: data,
+                        success: function (result) {
+                            resolve(result);
+                        },
+                        error: function (xhr, errorType, errorMessage) {
+                            console.log("There was an error updating the model: ", errorType, errorMessage, xhr);
+                            reject({response: xhr.responseJSON, errorType, errorMessage});
+                        }
+                    });
+                })
+            },
+            success: modelActions.processRequestSuccess,
+            loading: modelActions.processRequestInitiated,
+            error: modelActions.processRequestFailure
+        }
+    },
+    createModel: function () {
+        return {
+            remote: function (state, path, data) {
+                const url = path.reduce((url, node) => {
+                    url += node[0] !== '[' ? '/' : '';
+                    return url + node
+                }, `/model?path=/${state.path}`);                
+                return new Promise(function (resolve, reject) {
+                    $.ajax({
+                        url: url,
+                        type: 'PUT',
+                        data: data,
+                        success: function (result) {
+                            resolve(result);
+                        },
+                        error: function (xhr, errorType, errorMessage) {
+                            console.log("There was an error updating the model: ", errorType, errorMessage, xhr);
+                            reject({response: xhr.responseJSON, errorType, errorMessage});
+                        }
+                    });
+                })
+            },
+            success: modelActions.processRequestSuccess,
+            loading: modelActions.processRequestInitiated,
+            error: modelActions.processRequestFailure
+        }
+    },
+
+    deleteModel: function () {
+        return {
+            remote: function (state, path) {
+                const url = path.reduce((url, node) => {
+                    url += node[0] !== '[' ? '/' : '';
+                    return url + node
+                }, `/model?path=/${state.path}`);                
+                return new Promise(function (resolve, reject) {
+                    $.ajax({
+                        url: url,
+                        type: 'DELETE',
+                        success: function (result) {
+                            resolve(result);
+                        },
+                        error: function (xhr, errorType, errorMessage) {
+                            console.log("There was an error updating the model: ", errorType, errorMessage, xhr);
+                            reject({response: xhr.responseJSON, errorType, errorMessage});
+                        }
+                    });
+                })
+            },
+            success: modelActions.processRequestSuccess,
+            loading: modelActions.processRequestInitiated,
+            error: modelActions.processRequestFailure
+        }
+    }
+}
+module.exports = source;
\ No newline at end of file
diff --git a/skyquake/framework/source/schema/index.js b/skyquake/framework/source/schema/index.js
new file mode 100644
index 0000000..95b152e
--- /dev/null
+++ b/skyquake/framework/source/schema/index.js
@@ -0,0 +1,7 @@
+import schemaActions from './schemaActions'
+import schemaSource from './schemaSource'
+
+export {
+    schemaSource,
+    schemaActions
+}
\ No newline at end of file
diff --git a/skyquake/framework/source/schema/schemaActions.js b/skyquake/framework/source/schema/schemaActions.js
new file mode 100644
index 0000000..7e36cca
--- /dev/null
+++ b/skyquake/framework/source/schema/schemaActions.js
@@ -0,0 +1,31 @@
+/*
+ * 
+ *   Copyright 2017 RIFT.IO Inc
+ *
+ *   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.
+ *
+ */
+import Alt from 'widgets/skyquake_container/skyquakeAltInstance';
+
+class Actions {
+
+	constructor() {
+		this.generateActions(
+            'loadSchema',
+            'loadSchemaSuccess',
+            'loadSchemaLoading',
+            'loadSchemaFail');
+	}
+}
+
+export default Alt.createActions(Actions);
diff --git a/skyquake/framework/source/schema/schemaSource.js b/skyquake/framework/source/schema/schemaSource.js
new file mode 100644
index 0000000..2c7bd5b
--- /dev/null
+++ b/skyquake/framework/source/schema/schemaSource.js
@@ -0,0 +1,95 @@
+/*
+ * 
+ *   Copyright 2017 RIFT.IO Inc
+ *
+ *   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.
+ *
+ */
+import rw from 'utils/rw'
+import schemaActions from './schemaActions'
+import Utils from 'utils/utils'
+import $ from 'jquery';
+import StoreCache from '../SourceCache'
+
+const storeCache = new StoreCache('schema');
+storeCache.init(); // get the ball rolling
+
+function getCachedSchema(request) {
+    const cachedSchema = {};
+    const requestSchema = [];
+    request.forEach((path) => {
+        let schema = storeCache.get(path);
+        if (schema) {
+            cachedSchema[path] = schema
+        } else {
+            requestSchema.push(path);
+        }
+    });
+    return {
+        cachedSchema,
+        requestSchema
+    };
+}
+
+const schemaSource = {
+    loadSchema: function () {
+        return {
+            local: function (state, request) {
+                request = Array.isArray(request) ? request : [request];
+                const results = getCachedSchema(request);
+                if (!results.requestSchema.length) {
+                    return(Promise.resolve(results.cachedSchema));
+                }
+            }, 
+
+            remote: function (state, request) {
+                return new Promise(function (resolve, reject) {
+                    storeCache.init().then(() => {
+                        request = Array.isArray(request) ? request : [request];
+                        const results = getCachedSchema(request);
+                        if (!results.requestSchema.length) {
+                            resolve({
+                                schema: results.cachedSchema
+                            });
+                        } else {
+                            $.ajax({
+                                url: '/schema?request=' + results.requestSchema.join(','),
+                                type: 'GET',
+                                success: function ({
+                                    schema
+                                }) {
+                                    for (let path in schema) {
+                                        storeCache.set(path, schema[path]);
+                                    }
+                                    resolve(getCachedSchema(request).cachedSchema);
+                                },
+                                error: function (error) {
+                                    console.log("There was an error getting the schema: ", error);
+                                    reject(error);
+                                }
+                            }).fail(function (xhr) {
+                                console.log("There was an error getting the schema: ", xhr);
+                                Utils.checkAuthentication(xhr.status);
+                                reject("There was an error getting the schema.")
+                            });
+                        }
+                    })
+                })
+            },
+            success: schemaActions.loadSchemaSuccess,
+            loading: schemaActions.loadSchemaLoading,
+            error: schemaActions.loadSchemaFail
+        }
+    },
+}
+export default schemaSource;
\ No newline at end of file