diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 00000000..f6a1414a
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,26 @@
+As with all Open Source software, contributions to Exerciser are welcome.
+
+An excellent way to contribute is testing and trying Exerciser to find some issues. If you find one, don't be shy; submit your issue [here](https://github.com/llaske/Exerciser/issues) by giving the maximum information on it and precisely detailed steps to reproduce it. We will check your issue and ask you for more information if needed.
+
+If you're a developer, the best way is to first [setup Exerciser](https://github.com/llaske/ExerciserReact#steps-to-run-project) and then also [setup Sugarizer](https://github.com/llaske/sugarizer/blob/dev/docs/tutorial/VanillaJS/step0.md). Testing the functionality requires using Sugarizer features like `Sharing the Exercise,` `Using Journals,` and many more need Exerciser to be deployed on Sugarizer.
+
+When you think you're ready, you could try to fix some current issues [here](https://github.com/llaske/Exerciser/issues). If you find a fix, send a Pull Request, and we will be pleased to review it.
+
+
+So to send your Pull Request:
+
+* [ ] Clone this repository,
+* [ ] Fork the **master** branch of your new repository,
+* [ ] Update this new branch with your contribution and commit your changes,
+* [ ] Send a pull request from your new branch to the dev branch of this repository.
+
+Few rules to respect when you fix an issue:
+
+* [ ] Ensure your pull request contains only updates related to the fix,
+* [ ] Respect indentation of the original file,
+* [ ] Mention the issue number in the pull request but not in the commit message
+
+
+Please note there is no need to ask permission to work on an issue. You should check for pull requests linked to an issue you are addressing; if there are none, assume nobody has done anything. Begin to fix the problem, test, make your commits, push your commits, then make a pull request. These practices allow the competition of ideas and give priority to meritocracy.
+
+Thanks in advance for your contribution, and become a member of the Exerciser community!
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
index 261eeb9e..d6456956 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,3 +1,4 @@
+
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
diff --git a/NOTICE b/NOTICE
new file mode 100644
index 00000000..657b7a24
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,13 @@
+Copyright 2018 Mankirat Singh and Avinash Agarwal
+
+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.
diff --git a/config-overrides.js b/config-overrides.js
new file mode 100644
index 00000000..df49ff64
--- /dev/null
+++ b/config-overrides.js
@@ -0,0 +1,18 @@
+const CopyWebpackPlugin = require('copy-webpack-plugin');
+
+module.exports = function override(config, env) {
+ let resolvePlugin = (config.resolveLoader ? config.resolveLoader : {});
+ resolvePlugin['alias'] = {
+ 'text': 'text-loader'
+ };
+ config['resolveLoader'] = resolvePlugin;
+
+ let resolvePath = config.resolve.alias;
+ resolvePath['webL10n'] = 'lib/webL10n';
+ resolvePath['sugar-web'] = 'lib/sugar-web';
+ resolvePath['mustache'] = 'lib/mustache';
+ resolvePath['picoModal'] = 'lib/picoModal';
+ config.resolve.alias = resolvePath;
+
+ return config;
+}
\ No newline at end of file
diff --git a/lib/humane.js b/lib/humane.js
new file mode 100644
index 00000000..0764b4a4
--- /dev/null
+++ b/lib/humane.js
@@ -0,0 +1,240 @@
+/**
+ * humane.js
+ * Humanized Messages for Notifications
+ * @author Marc Harter (@wavded)
+ * @example
+ * humane.log('hello world');
+ * @license MIT
+ * See more usage examples at: http://wavded.github.com/humane-js/
+ */
+
+;!function (name, context, definition) {
+ // HACK: Force loading with define to avoid loading issue in Electron
+ //if (typeof module !== 'undefined') module.exports = definition(name, context)
+ //else if (typeof define === 'function' && typeof define.amd === 'object') define(definition)
+ //else context[name] = definition(name, context)
+ define(definition);
+}('humane', this, function (name, context) {
+ var win = window
+ var doc = document
+
+ var ENV = {
+ on: function (el, type, cb) {
+ 'addEventListener' in win ? el.addEventListener(type,cb,false) : el.attachEvent('on'+type,cb)
+ },
+ off: function (el, type, cb) {
+ 'removeEventListener' in win ? el.removeEventListener(type,cb,false) : el.detachEvent('on'+type,cb)
+ },
+ bind: function (fn, ctx) {
+ return function () { fn.apply(ctx,arguments) }
+ },
+ isArray: Array.isArray || function (obj) { return Object.prototype.toString.call(obj) === '[object Array]' },
+ config: function (preferred, fallback) {
+ return preferred != null ? preferred : fallback
+ },
+ transSupport: false,
+ useFilter: /msie [678]/i.test(navigator.userAgent), // sniff, sniff
+ _checkTransition: function () {
+ var el = doc.createElement('div')
+ var vendors = { webkit: 'webkit', Moz: '', O: 'o', ms: 'MS' }
+
+ for (var vendor in vendors)
+ if (vendor + 'Transition' in el.style) {
+ this.vendorPrefix = vendors[vendor]
+ this.transSupport = true
+ }
+ }
+ }
+ ENV._checkTransition()
+
+ var Humane = function (o) {
+ o || (o = {})
+ this.queue = []
+ this.baseCls = o.baseCls || 'humane'
+ this.addnCls = o.addnCls || ''
+ this.timeout = 'timeout' in o ? o.timeout : 2500
+ this.waitForMove = o.waitForMove || false
+ this.clickToClose = o.clickToClose || false
+ this.timeoutAfterMove = o.timeoutAfterMove || false
+ this.container = o.container
+
+ try { this._setupEl() } // attempt to setup elements
+ catch (e) {
+ ENV.on(win,'load',ENV.bind(this._setupEl, this)) // dom wasn't ready, wait till ready
+ }
+ }
+
+ Humane.prototype = {
+ constructor: Humane,
+ _setupEl: function () {
+ var el = doc.createElement('div')
+ el.style.display = 'none'
+ if (!this.container){
+ if(doc.body) this.container = doc.body;
+ else throw 'document.body is null'
+ }
+ this.container.appendChild(el)
+ this.el = el
+ this.removeEvent = ENV.bind(function(){
+ var timeoutAfterMove = ENV.config(this.currentMsg.timeoutAfterMove,this.timeoutAfterMove)
+ if (!timeoutAfterMove){
+ this.remove()
+ } else {
+ setTimeout(ENV.bind(this.remove,this),timeoutAfterMove)
+ }
+ },this)
+
+ this.transEvent = ENV.bind(this._afterAnimation,this)
+ this._run()
+ },
+ _afterTimeout: function () {
+ if (!ENV.config(this.currentMsg.waitForMove,this.waitForMove)) this.remove()
+
+ else if (!this.removeEventsSet) {
+ ENV.on(doc.body,'mousemove',this.removeEvent)
+ ENV.on(doc.body,'click',this.removeEvent)
+ ENV.on(doc.body,'keypress',this.removeEvent)
+ ENV.on(doc.body,'touchstart',this.removeEvent)
+ this.removeEventsSet = true
+ }
+ },
+ _run: function () {
+ if (this._animating || !this.queue.length || !this.el) return
+
+ this._animating = true
+ if (this.currentTimer) {
+ clearTimeout(this.currentTimer)
+ this.currentTimer = null
+ }
+
+ var msg = this.queue.shift()
+ var clickToClose = ENV.config(msg.clickToClose,this.clickToClose)
+
+ if (clickToClose) {
+ ENV.on(this.el,'click',this.removeEvent)
+ ENV.on(this.el,'touchstart',this.removeEvent)
+ }
+
+ var timeout = ENV.config(msg.timeout,this.timeout)
+
+ if (timeout > 0)
+ this.currentTimer = setTimeout(ENV.bind(this._afterTimeout,this), timeout)
+
+ if (ENV.isArray(msg.html)) msg.html = '
'+msg.html.join('
')+'
'
+
+ this.el.innerHTML = msg.html
+ this.currentMsg = msg
+ this.el.className = this.baseCls
+ if (ENV.transSupport) {
+ this.el.style.display = 'block'
+ setTimeout(ENV.bind(this._showMsg,this),50)
+ } else {
+ this._showMsg()
+ }
+
+ },
+ _setOpacity: function (opacity) {
+ if (ENV.useFilter){
+ try{
+ this.el.filters.item('DXImageTransform.Microsoft.Alpha').Opacity = opacity*100
+ } catch(err){}
+ } else {
+ this.el.style.opacity = String(opacity)
+ }
+ },
+ _showMsg: function () {
+ var addnCls = ENV.config(this.currentMsg.addnCls,this.addnCls)
+ if (ENV.transSupport) {
+ this.el.className = this.baseCls+' '+addnCls+' '+this.baseCls+'-animate'
+ }
+ else {
+ var opacity = 0
+ this.el.className = this.baseCls+' '+addnCls+' '+this.baseCls+'-js-animate'
+ this._setOpacity(0) // reset value so hover states work
+ this.el.style.display = 'block'
+
+ var self = this
+ var interval = setInterval(function(){
+ if (opacity < 1) {
+ opacity += 0.1
+ if (opacity > 1) opacity = 1
+ self._setOpacity(opacity)
+ }
+ else clearInterval(interval)
+ }, 30)
+ }
+ },
+ _hideMsg: function () {
+ var addnCls = ENV.config(this.currentMsg.addnCls,this.addnCls)
+ if (ENV.transSupport) {
+ this.el.className = this.baseCls+' '+addnCls
+ ENV.on(this.el,ENV.vendorPrefix ? ENV.vendorPrefix+'TransitionEnd' : 'transitionend',this.transEvent)
+ }
+ else {
+ var opacity = 1
+ var self = this
+ var interval = setInterval(function(){
+ if(opacity > 0) {
+ opacity -= 0.1
+ if (opacity < 0) opacity = 0
+ self._setOpacity(opacity);
+ }
+ else {
+ self.el.className = self.baseCls+' '+addnCls
+ clearInterval(interval)
+ self._afterAnimation()
+ }
+ }, 30)
+ }
+ },
+ _afterAnimation: function () {
+ if (ENV.transSupport) ENV.off(this.el,ENV.vendorPrefix ? ENV.vendorPrefix+'TransitionEnd' : 'transitionend',this.transEvent)
+
+ if (this.currentMsg.cb) this.currentMsg.cb()
+ this.el.style.display = 'none'
+
+ this._animating = false
+ this._run()
+ },
+ remove: function (e) {
+ var cb = typeof e == 'function' ? e : null
+
+ ENV.off(doc.body,'mousemove',this.removeEvent)
+ ENV.off(doc.body,'click',this.removeEvent)
+ ENV.off(doc.body,'keypress',this.removeEvent)
+ ENV.off(doc.body,'touchstart',this.removeEvent)
+ ENV.off(this.el,'click',this.removeEvent)
+ ENV.off(this.el,'touchstart',this.removeEvent)
+ this.removeEventsSet = false
+
+ if (cb && this.currentMsg) this.currentMsg.cb = cb
+ if (this._animating) this._hideMsg()
+ else if (cb) cb()
+ },
+ log: function (html, o, cb, defaults) {
+ var msg = {}
+ if (defaults)
+ for (var opt in defaults)
+ msg[opt] = defaults[opt]
+
+ if (typeof o == 'function') cb = o
+ else if (o)
+ for (var opt in o) msg[opt] = o[opt]
+
+ msg.html = html
+ if (cb) msg.cb = cb
+ this.queue.push(msg)
+ this._run()
+ return this
+ },
+ spawn: function (defaults) {
+ var self = this
+ return function (html, o, cb) {
+ self.log.call(self,html,o,cb,defaults)
+ return self
+ }
+ },
+ create: function (o) { return new Humane(o) }
+ }
+ return new Humane()
+});
diff --git a/lib/mustache.js b/lib/mustache.js
new file mode 100644
index 00000000..932052b4
--- /dev/null
+++ b/lib/mustache.js
@@ -0,0 +1,610 @@
+/*!
+ * mustache.js - Logic-less {{mustache}} templates with JavaScript
+ * http://github.com/janl/mustache.js
+ */
+
+/*global define: false*/
+
+(function (root, factory) {
+ if (typeof exports === "object" && exports) {
+ module.exports = factory; // CommonJS
+ } else if (typeof define === "function" && define.amd) {
+ define(factory); // AMD
+ } else {
+ root.Mustache = factory; //