<commit_before>window.onload = function() {
   loadOptions();
   updateTime();
};

var USER_OPTIONS = JSON.parse(localStorage.getItem('userOptions'));
var comLinks = {
   'r':'https://www.reddit.com/r/',
   'g':'https://www.google.com/search?q=',
   'y':'https://www.youtube.com/results?search_query=',
   'a':'http://smile.amazon.com/s?url=search-alias%3Daps&field-keywords=',
   'w':'https://www.wikipedia.org/w/index.php?title=Special:Search&search=',
   'wa':'http://www.wolframalpha.com/input/?i=',
   'imdb':'https://www.imdb.com/find?s=all&q=',
   't':'',
}

function interpret() {
   inputElem = document.getElementById('commandInput');
   inputElem.select();
   var input = inputElem.value;
   if (input == '') { return; }

   // Check for available commands
   if (input.trim() === 'help') {
      displayHelp();
      return;
   } else if (input.trim() === 'options') {
      displayOptions();
      return;
   }

   // Parse input
   var inputArray = input.split(';');
   var nt;
   if (inputArray[inputArray.length-1] === 'n' || USER_OPTIONS.alwaysNewTab) {
      nt = true;
   } else {
      nt = false;
   }
   switch(inputArray.length) {
      case 1:
         command = USER_OPTIONS.defaultCommand;
         query = inputArray[0].trim();
         break;
      case 2:
         if (inputArray[inputArray.length-1] !== 'n') {
            command = inputArray[0].trim();
            query = inputArray[1].trim();
            break;
         } else {
            command = USER_OPTIONS.defaultCommand;
            query = inputArray[0];
            break;
         }
      case 3:
         command = inputArray[0].trim();
         query = inputArray[1].trim();
         // Assume inputArray[2] is new tab flag
         break;
      default:
         displayText('unable to interpret');
   }
   if (command === 'w') { // Wikipedia fix
      query = query.replace(/ /g,'+'); // replace all spaces with +'s
   } else {
      query = encodeURIComponent(query);
   }
   if (comLinks.hasOwnProperty(command)) {
      goToSite(comLinks[command] + query, nt);
      return false;
   } else {
      displayText('command not recognized');
      return;
   }
}

function verifyKey(e) {
   var keycode;
   if (window.event) {
      keycode = window.event.keyCode;
   } else if (e) {
      keycode = e.which;
   }
   if (keycode == 13) {
      clearText();
      interpret();
   }
   updateInputLength();
}

function updateInputLength() {
   var entry_len = document.getElementById('commandInput').value.length;
   document.getElementById('commandInput').size = entry_len + 2;
}

function goToSite(url, nt) {
   if (!url.startsWith('http')) {
      url = 'https://'+url;
   }
   if (nt) {
      var win = window.open(url);
      win.focus();
      return false;
   } else {
      window.location.replace(url);
      return false;
   }
}

function displayText(string) {
   var div = document.getElementById('text_div');
   clearText();
   div.innerHTML += '
'+string+'
';
}

function clearText() {
   document.getElementById('text_div').innerHTML = '';
}

function applyBgColor(c) {
   document.body.style.backgroundColor = c;
   document.getElementById('commandInput').style.backgroundColor = c;
}

function saveOptions() {
   if (typeof(Storage) !== "undefined") {
      // Default command
      var defaultCommandRadios = document.getElementById('defaultCommandForm');
      var defaultCommand = null;
      for (var i=0; i < defaultCommandRadios.length; i++) {
         if (defaultCommandRadios[i].checked) {
            defaultCommand = defaultCommandRadios[i].value;
            break;
         }
      }
      if (defaultCommand !== null) {
         USER_OPTIONS.defaultCommand = defaultCommand;
      }

      // Tab open style
      var alwaysNewTab = document.getElementById('alwaysNewTabCheckbox').checked;
      USER_OPTIONS.alwaysNewTab = alwaysNewTab;

      // Background color
      var bgColor = document.getElementById('bgColorInput').value;
            // check if valid hex value
      if (/(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(bgColor)) {
         USER_OPTIONS.bgColor = bgColor;
         applyBgColor(bgColor);

         // Store prefs
         localStorage.setItem('userOptions', JSON.stringify(USER_OPTIONS));
         document.getElementById('optionsSubText').innerHTML = 'settings saved';
      } else {
         document.getElementById('optionsSubText').innerHTML = 'not a valid color';
      }

   } else {
      alert("Browser does not support local storage: your settings won't be saved (sorry)");
   }
}

function loadOptions() {
   if (typeof(Storage) !== "undefined") {
      if (localStorage.getItem('userOptions') !== null) { // Load options
         var userOptions = localStorage.getItem('userOptions');
         userOptions = JSON.parse(userOptions);
         USER_OPTIONS = userOptions;
         applyBgColor(USER_OPTIONS.bgColor);
         return userOptions;
      } else { // Create defaults
         var options = {
            "defaultCommand": "g",
            "alwaysNewTab": false,
            "bgColor": "#A1C0C0"
         };
         localStorage.setItem('userOptions', JSON.stringify(options));
      }
   }
}

function displayOptions() {
   var optionsHTML =
      `

         Default Command (command that executes if no command was specified)

            <form id='defaultCommandForm'>
               <input type='radio' name='defaultCommandRadio' value='t'>Go to website

               <input type='radio' name='defaultCommandRadio' value='g'>Google search

               <input type='radio' name='defaultCommandRadio' value='w'>Wikipedia search

               <input type='radio' name='defaultCommandRadio' value='y'>YouTube search

               <input type='radio' name='defaultCommandRadio' value='a'>Amazon search

               <input type='radio' name='defaultCommandRadio' value='imdb'>IMDB search

               <input type='radio' name='defaultCommandRadio' value='wa'>Wolfram Alpha search

               <input type='radio' name='defaultCommandRadio' value='r'>Go to subreddit
            </form>

         Open Style

            <input type='checkbox' id='alwaysNewTabCheckbox' value='newTab'>Always open in new tab

         Background Color

            Color: <input type='text' id='bgColorInput' placeholder='#A1C0C0' size='7' spellcheck='false'>
            Color Picker

            <button type='button' id='saveOptionsBtn'>Save</button>

      `
   document.getElementById('text_div').innerHTML = optionsHTML;
   document.getElementById('saveOptionsBtn').onclick = saveOptions;

   // Show saved options
   var defaultCommandRadios = document.getElementById('defaultCommandForm');
   for (var i=0; i < defaultCommandRadios.length; i++) {
      if (USER_OPTIONS.defaultCommand === defaultCommandRadios[i].value) {
         defaultCommandRadios[i].checked = true;
      }
   }
   document.getElementById('alwaysNewTabCheckbox').checked = USER_OPTIONS.alwaysNewTab;
   document.getElementById('bgColorInput').value = USER_OPTIONS.bgColor;
}

function displayHelp() {
   var helpHTML =
      `

syntax: command;query[;n]

            Command
            Description

            options
            show options menu

            r;&ltsubreddit&gt
            go to subreddit

            g;&ltquery&gt
            google search

            y;&ltquery&gt
            youtube search

            a;&ltquery&gt
            amazon search

            w;&ltquery&gt
            wikipedia search

            imdb;&ltquery&gt
            imdb search

            wa;&ltquery&gt
            wolfram alpha search

            t;&lturl&gt
            open url in current tab

            &ltcommand&gt;n
            open action in new tab

            help
            show this text

`
   document.getElementById('text_div').innerHTML = helpHTML;
}

function checkTime(i) {
   if (i < 10) { i = '0' + i };
   if (i == 0) { i = '12' };
   return i;
}
function updateTime() {
   var d = new Date();
   var h = d.getHours(); if (h > 12) { h -= 12; }; h = checkTime(h);
   var m = checkTime(d.getMinutes());
   document.getElementById('time_div').innerHTML = "
"+h+':'+m+'
';
   var t = setTimeout(updateTime, 5000);
}
<commit_msg>added bottom border to command input<commit_after>window.onload = function() {
   loadOptions();
   updateTime();
};

var USER_OPTIONS = JSON.parse(localStorage.getItem('userOptions'));
var comLinks = {
   'r':'https://www.reddit.com/r/',
   'g':'https://www.google.com/search?q=',
   'y':'https://www.youtube.com/results?search_query=',
   'a':'http://smile.amazon.com/s?url=search-alias%3Daps&field-keywords=',
   'w':'https://www.wikipedia.org/w/index.php?title=Special:Search&search=',
   'wa':'http://www.wolframalpha.com/input/?i=',
   'imdb':'https://www.imdb.com/find?s=all&q=',
   't':'',
}

function interpret() {
   inputElem = document.getElementById('commandInput');
   inputElem.select();
   var input = inputElem.value;
   if (input == '') { return; }

   // Check for available commands
   if (input.trim() === 'help') {
      displayHelp();
      return;
   } else if (input.trim() === 'options') {
      displayOptions();
      return;
   }

   // Parse input
   var inputArray = input.split(';');
   var nt;
   if (inputArray[inputArray.length-1] === 'n' || USER_OPTIONS.alwaysNewTab) {
      nt = true;
   } else {
      nt = false;
   }
   switch(inputArray.length) {
      case 1:
         command = USER_OPTIONS.defaultCommand;
         query = inputArray[0].trim();
         break;
      case 2:
         if (inputArray[inputArray.length-1] !== 'n') {
            command = inputArray[0].trim();
            query = inputArray[1].trim();
            break;
         } else {
            command = USER_OPTIONS.defaultCommand;
            query = inputArray[0];
            break;
         }
      case 3:
         command = inputArray[0].trim();
         query = inputArray[1].trim();
         // Assume inputArray[2] is new tab flag
         break;
      default:
         displayText('unable to interpret');
   }
   if (command === 'w') { // Wikipedia fix
      query = query.replace(/ /g,'+'); // replace all spaces with +'s
   } else {
      query = encodeURIComponent(query);
   }
   if (comLinks.hasOwnProperty(command)) {
      goToSite(comLinks[command] + query, nt);
      return false;
   } else {
      displayText('command not recognized');
      return;
   }
}

function verifyKey(e) {
   var keycode;
   if (window.event) {
      keycode = window.event.keyCode;
   } else if (e) {
      keycode = e.which;
   }
   if (keycode == 13) { // Enter/return key
      clearText();
      interpret();
   }
   updateInputLength();
}

function updateInputLength() {
   var input = document.getElementById('commandInput');
   if (input.value.length >= (input.size - 1)) {
      input.size = input.value.length + 1;
   }
}

function goToSite(url, nt) {
   if (!url.startsWith('http')) {
      url = 'https://'+url;
   }
   if (nt) {
      var win = window.open(url);
      win.focus();
      return false;
   } else {
      window.location.replace(url);
      return false;
   }
}

function displayText(string) {
   var div = document.getElementById('text_div');
   clearText();
   div.innerHTML += '
'+string+'
';
}

function clearText() {
   document.getElementById('text_div').innerHTML = '';
}

function applyBgColor(c) {
   document.body.style.backgroundColor = c;
   //document.getElementById('commandInput').style.backgroundColor = c;
}

function saveOptions() {
   if (typeof(Storage) !== "undefined") {
      // Default command
      var defaultCommandRadios = document.getElementById('defaultCommandForm');
      var defaultCommand = null;
      for (var i=0; i < defaultCommandRadios.length; i++) {
         if (defaultCommandRadios[i].checked) {
            defaultCommand = defaultCommandRadios[i].value;
            break;
         }
      }
      if (defaultCommand !== null) {
         USER_OPTIONS.defaultCommand = defaultCommand;
      }

      // Tab open style
      var alwaysNewTab = document.getElementById('alwaysNewTabCheckbox').checked;
      USER_OPTIONS.alwaysNewTab = alwaysNewTab;

      // Background color
      var bgColor = document.getElementById('bgColorInput').value;
            // check if valid hex value
      if (/(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(bgColor)) {
         USER_OPTIONS.bgColor = bgColor;
         applyBgColor(bgColor);

         // Store prefs
         localStorage.setItem('userOptions', JSON.stringify(USER_OPTIONS));
         document.getElementById('optionsSubText').innerHTML = 'settings saved';
      } else {
         document.getElementById('optionsSubText').innerHTML = 'not a valid color';
      }

   } else {
      alert("Browser does not support local storage: your settings won't be saved (sorry)");
   }
}

function loadOptions() {
   if (typeof(Storage) !== "undefined") {
      if (localStorage.getItem('userOptions') !== null) { // Load options
         var userOptions = localStorage.getItem('userOptions');
         userOptions = JSON.parse(userOptions);
         USER_OPTIONS = userOptions;
         applyBgColor(USER_OPTIONS.bgColor);
         return userOptions;
      } else { // Create defaults
         var options = {
            "defaultCommand": "g",
            "alwaysNewTab": false,
            "bgColor": "#A1C0C0"
         };
         localStorage.setItem('userOptions', JSON.stringify(options));
      }
   }
}

function displayOptions() {
   var optionsHTML =
      `

         Default Command (command that executes if no command was specified)

            <form id='defaultCommandForm'>
               <input type='radio' name='defaultCommandRadio' value='t'>Go to website

               <input type='radio' name='defaultCommandRadio' value='g'>Google search

               <input type='radio' name='defaultCommandRadio' value='w'>Wikipedia search

               <input type='radio' name='defaultCommandRadio' value='y'>YouTube search

               <input type='radio' name='defaultCommandRadio' value='a'>Amazon search

               <input type='radio' name='defaultCommandRadio' value='imdb'>IMDB search

               <input type='radio' name='defaultCommandRadio' value='wa'>Wolfram Alpha search

               <input type='radio' name='defaultCommandRadio' value='r'>Go to subreddit
            </form>

         Open Style

            <input type='checkbox' id='alwaysNewTabCheckbox' value='newTab'>Always open in new tab

         Background Color

            Color: <input type='text' id='bgColorInput' placeholder='#A1C0C0' size='7' spellcheck='false'>
            Color Picker

            <button type='button' id='saveOptionsBtn'>Save</button>

      `
   document.getElementById('text_div').innerHTML = optionsHTML;
   document.getElementById('saveOptionsBtn').onclick = saveOptions;

   // Show saved options
   var defaultCommandRadios = document.getElementById('defaultCommandForm');
   for (var i=0; i < defaultCommandRadios.length; i++) {
      if (USER_OPTIONS.defaultCommand === defaultCommandRadios[i].value) {
         defaultCommandRadios[i].checked = true;
      }
   }
   document.getElementById('alwaysNewTabCheckbox').checked = USER_OPTIONS.alwaysNewTab;
   document.getElementById('bgColorInput').value = USER_OPTIONS.bgColor;
}

function displayHelp() {
   var helpHTML =
      `

syntax: command;query[;n]

            Command
            Description

            options
            show options menu

            r;&ltsubreddit&gt
            go to subreddit

            g;&ltquery&gt
            google search

            y;&ltquery&gt
            youtube search

            a;&ltquery&gt
            amazon search

            w;&ltquery&gt
            wikipedia search

            imdb;&ltquery&gt
            imdb search

            wa;&ltquery&gt
            wolfram alpha search

            t;&lturl&gt
            open url in current tab

            &ltcommand&gt;n
            open action in new tab

            help
            show this text

`
   document.getElementById('text_div').innerHTML = helpHTML;
}

function checkTime(i) {
   if (i < 10) { i = '0' + i };
   if (i == 0) { i = '12' };
   return i;
}
function updateTime() {
   var d = new Date();
   var h = d.getHours(); if (h > 12) { h -= 12; }; h = checkTime(h);
   var m = checkTime(d.getMinutes());
   document.getElementById('time_div').innerHTML = "
"+h+':'+m+'
';
   var t = setTimeout(updateTime, 5000);
}
<|endoftext|>


<commit_before>/**
 * @author Phuluong
 * December 27, 2015
 */

/** Exports **/
module.exports = new RouteLoader();
/** Imports **/
var IO = require("../io/io");
var util = require(__dir + "/core/app/util");
var config = require(__dir + "/core/app/config");
var contentTypes = require(__dir + "/core/io/content-types");
/** Modules **/
function RouteLoader() {
    this.filterContainer = [];
    this.load = function (constructorProperties) {
        this.sessionManager = constructorProperties.sessionManager;
        this.socketIOConnection = constructorProperties.socketIOConnection;
        this.httpConnection = constructorProperties.httpConnection;
        this.autoLoader = constructorProperties.autoLoader;
        this.viewEngine = constructorProperties.viewEngine;
        this.httpConnection.asset(processAssetRequest);
        this.initHTTPRoutes();
    };
    this.any = function (routeName, route, filters) {
        this.io(routeName, route, filters);
        for (var i = 0; i < this.httpConnection.methods.length; i++) {
            this[this.httpConnection.methods[i]](routeName, route, filters);
        }
        return this;
    };
    this.io = function (routeName, action, filters) {
        var self = this;
        this.socketIOConnection.addMessageListener(routeName, function (data, session) {
            var io = new IO({
                method: "io",
                autoLoader: self.autoLoader,
                routeName: routeName,
                sessionManager: self.sessionManager,
                viewEngine: self.viewEngine
            });
            io.bindSocketIO(data, session);
            executeAction(self, action, io, filters);
        });
        return this;
    };
    this.initHTTPRoutes = function () {
        var self = this;
        for (var i = 0; i < this.httpConnection.methods.length; i++) {
            var method = this.httpConnection.methods[i];
            this[method] = function (self, method) {
                return function (routeName, action, filters) {
                    self.httpConnection[method](routeName, function (req, res) {
                        var io = new IO({
                            method: method,
                            autoLoader: self.autoLoader,
                            routeName: routeName,
                            sessionManager: self.sessionManager,
                            viewEngine: self.viewEngine
                        });
                        io.bindHttp(req, res);
                        executeAction(self, action, io, filters);
                    });
                    return self;
                };
            }(this, method);
        }
    }
    /**
     * Register a filter to the route
     * @param {String} name
     * @param {callable} callbackFn
     * @returns {RouteLoader}
     */
    this.filter = function (name, callbackFn) {
        this.filterContainer[name] = callbackFn;
        return this;
    };
    function executeAction(self, action, io, filters) {
        var interrupt = false;
        // call before-filter
        if (filters != null && filters.before != null) {
            if (typeof filters.before === "function") {
                if (filters.before(io) === false) {
                    interrupt = true;
                }
            } else if (typeof filters.before === "string") {
                if (self.filterContainer[filters.before] != null && self.filterContainer[filters.before](io) === false) {
                    interrupt = true;
                }
            } else if ((typeof filters.before === "object") && filters.before.length > 0) {
                for (var i = 0; i < filters.before.length; i++) {
                    if (typeof filters.before[i] === "function") {
                        if (filters.before[i](io) === false) {
                            interrupt = true;
                        }
                    } else if (typeof filters.before[i] === "string") {
                        if (self.filterContainer[filters.before[i]] != null && self.filterContainer[filters.before[i]](io) === false) {
                            interrupt = true;
                        }
                    }
                }
            }

        }
        // if before-filter return false, return before executing action
        if (interrupt) {
            return;
        }
        // call action
        if (typeof action === "function") {
            action(io);
        } else if (typeof action === "string") {
            var actionFn = self.autoLoader.getAction(action);
            if (actionFn != null) {
                actionFn(io);
            } else {
                io.status(404).json({
                    status: 404,
                    result: "page not found"
                });
            }
        }
        // call after-filter
        if (filters != null && filters.after != null) {
            if (typeof filters.after === "function") {
                filters.after(io);
            } else if (typeof filters.after === "string") {
                if (self.filterContainer[filters.after] != null) {
                    self.filterContainer[filters.after](io);
                }
            } else if ((typeof filters.after === "object") && filters.after.length > 0) {
                for (var i = 0; i < filters.after.length; i++) {
                    if (typeof filters.after[i] === "function") {
                        filters.after[i](io);
                    } else if (typeof filters.after[i] === "string") {
                        if (self.filterContainer[filters.after[i]] != null) {
                            self.filterContainer[filters.after[i]](io);
                        }
                    }
                }
            }
        }
    }
    function processAssetRequest(req, res) {
        var result = util.readFile(__dir + config.get("app.assetPath", "") + req.baseUrl);
        if (result === false) {
            res.writeHead(404, {"Content-Type": "application/json"});
            res.end(JSON.stringify({
                status: 404,
                result: "page not found"
            }));
        } else {
            var header = {"Connection": "close"};
            var fileExtension = req.baseUrl.fileExtension();
            var contentType = contentTypes[fileExtension];
            if (contentType != null) {
                header["Content-Type"] = contentType;
            }
            res.writeHead(200, header);
            res.end(result, "binary");
        }
    }
}
<commit_msg>add feature: route group example: $route.group(function() { $route.any("/r1", function(io) { io.echo("r1"); }); $route.get("/r2", function(io) { io.echo("r2"); }); },{ before: ["auth"], after: ["log"] });<commit_after>/**
 * @author Phuluong
 * December 27, 2015
 */

/** Exports **/
module.exports = new RouteLoader();
/** Imports **/
var IO = require("../io/io");
var util = require(__dir + "/core/app/util");
var config = require(__dir + "/core/app/config");
var contentTypes = require(__dir + "/core/io/content-types");
/** Modules **/
function RouteLoader() {
    this.filterContainer = [];
    this.load = function (constructorProperties) {
        this.sessionManager = constructorProperties.sessionManager;
        this.socketIOConnection = constructorProperties.socketIOConnection;
        this.httpConnection = constructorProperties.httpConnection;
        this.autoLoader = constructorProperties.autoLoader;
        this.viewEngine = constructorProperties.viewEngine;
        this.httpConnection.asset(processAssetRequest);
        this.initHTTPRoutes();
    };

    /**
     * Grouped routes
     * @param {callable} routes
     * @param {} filters
     * @returns {RouteLoader}
     */
    this.group = function (routes, filters) {
        this.groupFilters = filters;
        routes.bind(this)();
        return this;
    };

    /**
    * Register a multi method route
    * @param {String} routeName
    * @param {callable | String} action
    * @param {} filters
    * @returns {RouteLoader}
    */
    this.any = function (routeName, route, filters) {
        this.io(routeName, route, filters);
        for (var i = 0; i < this.httpConnection.methods.length; i++) {
            this[this.httpConnection.methods[i]](routeName, route, filters);
        }
        return this;
    };

    /**
    * Register a socket.io route
    * @param {String} routeName
    * @param {callable | String} action
    * @param {} filters
    * @returns {RouteLoader}
    */
    this.io = function (routeName, action, filters) {
        var self = this;
        this.socketIOConnection.addMessageListener(routeName, function (data, session) {
            var io = new IO({
                method: "io",
                autoLoader: self.autoLoader,
                routeName: routeName,
                sessionManager: self.sessionManager,
                viewEngine: self.viewEngine
            });
            io.bindSocketIO(data, session);
            executeAction(self, action, io, filters);
        });
        return this;
    };

    /**
    * Initialize HTTP route registers
    * @param {String} routeName
    * @param {callable | String} action
    * @param {} filters
    * @returns {RouteLoader}
    */
    this.initHTTPRoutes = function () {
        var self = this;
        for (var i = 0; i < this.httpConnection.methods.length; i++) {
            var method = this.httpConnection.methods[i];
            this[method] = function (self, method) {
                return function (routeName, action, filters) {
                    if (filters == null && self.groupFilters != null) {
                        filters = self.groupFilters;
                    }
                    self.httpConnection[method](routeName, function (req, res) {
                        var io = new IO({
                            method: method,
                            autoLoader: self.autoLoader,
                            routeName: routeName,
                            sessionManager: self.sessionManager,
                            viewEngine: self.viewEngine
                        });
                        io.bindHttp(req, res);
                        executeAction(self, action, io, filters);
                    });
                    return self;
                };
            }(this, method);
        }
    }
    /**
     * Register a filter to the route
     * @param {String} name
     * @param {callable} callbackFn
     * @returns {RouteLoader}
     */
    this.filter = function (name, callbackFn) {
        this.filterContainer[name] = callbackFn;
        return this;
    };
    function executeAction(self, action, io, filters) {
        var interrupt = false;
        // call before-filter
        if (filters != null && filters.before != null) {
            if (typeof filters.before === "function") {
                if (filters.before(io) === false) {
                    interrupt = true;
                }
            } else if (typeof filters.before === "string") {
                if (self.filterContainer[filters.before] != null && self.filterContainer[filters.before](io) === false) {
                    interrupt = true;
                }
            } else if ((typeof filters.before === "object") && filters.before.length > 0) {
                for (var i = 0; i < filters.before.length; i++) {
                    if (typeof filters.before[i] === "function") {
                        if (filters.before[i](io) === false) {
                            interrupt = true;
                        }
                    } else if (typeof filters.before[i] === "string") {
                        if (self.filterContainer[filters.before[i]] != null && self.filterContainer[filters.before[i]](io) === false) {
                            interrupt = true;
                        }
                    }
                }
            }

        }
        // if before-filter return false, return before executing action
        if (interrupt) {
            return;
        }
        // call action
        if (typeof action === "function") {
            action(io);
        } else if (typeof action === "string") {
            var actionFn = self.autoLoader.getAction(action);
            if (actionFn != null) {
                actionFn(io);
            } else {
                io.status(404).json({
                    status: 404,
                    result: "page not found"
                });
            }
        }
        // call after-filter
        if (filters != null && filters.after != null) {
            if (typeof filters.after === "function") {
                filters.after(io);
            } else if (typeof filters.after === "string") {
                if (self.filterContainer[filters.after] != null) {
                    self.filterContainer[filters.after](io);
                }
            } else if ((typeof filters.after === "object") && filters.after.length > 0) {
                for (var i = 0; i < filters.after.length; i++) {
                    if (typeof filters.after[i] === "function") {
                        filters.after[i](io);
                    } else if (typeof filters.after[i] === "string") {
                        if (self.filterContainer[filters.after[i]] != null) {
                            self.filterContainer[filters.after[i]](io);
                        }
                    }
                }
            }
        }
    }
    function processAssetRequest(req, res) {
        var result = util.readFile(__dir + config.get("app.assetPath", "") + req.baseUrl);
        if (result === false) {
            res.writeHead(404, {"Content-Type": "application/json"});
            res.end(JSON.stringify({
                status: 404,
                result: "page not found"
            }));
        } else {
            var header = {"Connection": "close"};
            var fileExtension = req.baseUrl.fileExtension();
            var contentType = contentTypes[fileExtension];
            if (contentType != null) {
                header["Content-Type"] = contentType;
            }
            res.writeHead(200, header);
            res.end(result, "binary");
        }
    }
}
<|endoftext|>


<commit_before>class TkSwitcherElement extends HTMLElement {
  /* Attributes to monitor */
  static get observedAttributes() { return ['type', 'off-text', 'on-text']; }
  get type() { return this.getAttribute('type'); }
  set type(value) { return this.setAttribute('type', value); }
  get offText() { return this.getAttribute('off-text') || 'Off'; }
  get onText() { return this.getAttribute('on-text') || 'On'; }

  constructor() {
    super();

    this.inputs = [];
    this.spans = [];
    this.inputsContainer = '';
    this.spansContainer = '';
    this.newActive = '';
  }

  /* Lifecycle, element appended to the DOM */
  connectedCallback() {
    this.inputs = [].slice.call(this.querySelectorAll('input'));

    if (this.inputs.length !== 2 || this.inputs[0].type !== 'radio') {
      throw new Error('`Tk-switcher` requires two inputs type="checkbox"');
    }

    // Create the markup
    this.createMarkup.bind(this)();

    this.inputsContainer = this.firstElementChild;
    this.spansContainer = this.lastElementChild;

    this.inputsContainer.setAttribute('role', 'switch');

    if (this.inputs[1].checked) {
      this.inputs[1].parentNode.classList.add('active');
      this.spans[1].classList.add('active');

      // Aria-label ONLY in the container span!
      this.inputsContainer.setAttribute('aria-label', this.spans[1].innerHTML);
    } else {
      this.spans[0].classList.add('active');

      // Aria-label ONLY in the container span!
      this.inputsContainer.setAttribute('aria-label', this.spans[0].innerHTML);
    }

    this.inputs.forEach((switchEl, index) => {
      // Add the active class on click
      switchEl.addEventListener('click', this.toggle.bind(this));
    });

    this.inputsContainer.addEventListener('keydown', this.keyEvents.bind(this));
  }

  /* Lifecycle, element removed from the DOM */
  disconnectedCallback() {
    this.removeEventListener('tk.switcher.toggle', this.toggle, true);
    this.removeEventListener('click', this.switch, true);
    this.removeEventListener('keydown', this.keydown, true);
  }

  /* Method to dispatch events */
  dispatchCustomEvent(eventName) {
    const OriginalCustomEvent = new CustomEvent(eventName, { bubbles: true, cancelable: true });
    OriginalCustomEvent.relatedTarget = this;
    this.dispatchEvent(OriginalCustomEvent);
    this.removeEventListener(eventName, this);
  }

  /** Method to build the switch */
  createMarkup() {
    let checked = 0;

    // Create the first 'span' wrapper
    const spanFirst = document.createElement('span');
    spanFirst.classList.add('switcher');
    spanFirst.setAttribute('tabindex', 0);

    // If no type has been defined, the default as "success"
    if (!this.type) {
      this.setAttribute('type', 'success');
    }

    const switchEl = document.createElement('span');
    switchEl.classList.add('switch');

    this.inputs.forEach((input, index) => {
      // Remove the tab focus from the inputs
      input.setAttribute('tabindex', '-1');

      if (input.checked) {
        spanFirst.setAttribute('aria-checked', true);
      }

      spanFirst.appendChild(input);

      if (index === 1 && input.checked) {
        checked = 1;
      }
    });

    spanFirst.appendChild(switchEl);

    // Create the second 'span' wrapper
    const spanSecond = document.createElement('span');
    spanSecond.classList.add('switcher-labels');

    const labelFirst = document.createElement('span');
    labelFirst.classList.add('switcher-label-0');
    labelFirst.innerText = this.offText;

    const labelSecond = document.createElement('span');
    labelSecond.classList.add('switcher-label-1');
    labelSecond.innerText = this.onText;

    if (checked === 0) {
      labelFirst.classList.add('active');
    } else {
      labelSecond.classList.add('active');
    }

    this.spans.push(labelFirst);
    this.spans.push(labelSecond);
    spanSecond.appendChild(labelFirst);
    spanSecond.appendChild(labelSecond);

    // Append everything back to the main element
    this.appendChild(spanFirst);
    this.appendChild(spanSecond);
  }

  /** Method to toggle the switch */
  switch() {
    this.spans.forEach((span) => {
      span.classList.remove('active');
    });

    if (this.inputsContainer.classList.contains('active')) {
      this.inputsContainer.classList.remove('active');
    } else {
      this.inputsContainer.classList.add('active');
    }

    // Remove active class from all inputs
    this.inputs.forEach((input) => {
      input.classList.remove('active');
    });

    // Check if active
    if (this.newActive === 1) {
      this.inputs[this.newActive].classList.add('active');
      this.inputs[1].setAttribute('checked', '');
      this.inputs[0].removeAttribute('checked');
      this.inputsContainer.setAttribute('aria-checked', true);

      // Aria-label ONLY in the container span!
      this.inputsContainer.setAttribute('aria-label', this.spans[1].innerHTML);

      // Dispatch the "tk.switcher.on" event
      this.dispatchCustomEvent('tk.switcher.on');
    } else {
      this.inputs[1].removeAttribute('checked');
      this.inputs[0].setAttribute('checked', '');
      this.inputs[0].classList.add('active');
      this.inputsContainer.setAttribute('aria-checked', false);

      // Aria-label ONLY in the container span!
      this.inputsContainer.setAttribute('aria-label', this.spans[0].innerHTML);

      // Dispatch the "tk.switcher.off" event
      this.dispatchCustomEvent('tk.switcher.off');
    }

    this.spans[this.newActive].classList.add('active');
  }

  /** Method to toggle the switch */
  toggle(e) {
    e.preventDefault();
    this.newActive = this.inputs[1].classList.contains('active') ? 0 : 1;

    this.switch.bind(this)();
  }

  keyEvents(event) {
    if (event.keyCode === 13 || event.keyCode === 32) {
      event.preventDefault();
      this.newActive = this.inputs[1].classList.contains('active') ? 0 : 1;

      this.switch.bind(this)();
    }
  }
}

customElements.define('tk-switcher', TkSwitcherElement);
<commit_msg>fix another travis error<commit_after>class TkSwitcherElement extends HTMLElement {
  /* Attributes to monitor */
  static get observedAttributes() { return ['type', 'off-text', 'on-text']; }
  get type() { return this.getAttribute('type'); }
  set type(value) { return this.setAttribute('type', value); }
  get offText() { return this.getAttribute('off-text') || 'Off'; }
  get onText() { return this.getAttribute('on-text') || 'On'; }

  constructor() {
    super();

    this.inputs = [];
    this.spans = [];
    this.inputsContainer = '';
    this.spansContainer = '';
    this.newActive = '';
  }

  /* Lifecycle, element appended to the DOM */
  connectedCallback() {
    this.inputs = [].slice.call(this.querySelectorAll('input'));

    if (this.inputs.length !== 2 || this.inputs[0].type !== 'radio') {
      throw new Error('`Tk-switcher` requires two inputs type="checkbox"');
    }

    // Create the markup
    this.createMarkup.bind(this)();

    this.inputsContainer = this.firstElementChild;
    this.spansContainer = this.lastElementChild;

    this.inputsContainer.setAttribute('role', 'switch');

    if (this.inputs[1].checked) {
      this.inputs[1].parentNode.classList.add('active');
      this.spans[1].classList.add('active');

      // Aria-label ONLY in the container span!
      this.inputsContainer.setAttribute('aria-label', this.spans[1].innerHTML);
    } else {
      this.spans[0].classList.add('active');

      // Aria-label ONLY in the container span!
      this.inputsContainer.setAttribute('aria-label', this.spans[0].innerHTML);
    }

    this.inputs.forEach((switchEl) => {
      // Add the active class on click
      switchEl.addEventListener('click', this.toggle.bind(this));
    });

    this.inputsContainer.addEventListener('keydown', this.keyEvents.bind(this));
  }

  /* Lifecycle, element removed from the DOM */
  disconnectedCallback() {
    this.removeEventListener('tk.switcher.toggle', this.toggle, true);
    this.removeEventListener('click', this.switch, true);
    this.removeEventListener('keydown', this.keydown, true);
  }

  /* Method to dispatch events */
  dispatchCustomEvent(eventName) {
    const OriginalCustomEvent = new CustomEvent(eventName, { bubbles: true, cancelable: true });
    OriginalCustomEvent.relatedTarget = this;
    this.dispatchEvent(OriginalCustomEvent);
    this.removeEventListener(eventName, this);
  }

  /** Method to build the switch */
  createMarkup() {
    let checked = 0;

    // Create the first 'span' wrapper
    const spanFirst = document.createElement('span');
    spanFirst.classList.add('switcher');
    spanFirst.setAttribute('tabindex', 0);

    // If no type has been defined, the default as "success"
    if (!this.type) {
      this.setAttribute('type', 'success');
    }

    const switchEl = document.createElement('span');
    switchEl.classList.add('switch');

    this.inputs.forEach((input, index) => {
      // Remove the tab focus from the inputs
      input.setAttribute('tabindex', '-1');

      if (input.checked) {
        spanFirst.setAttribute('aria-checked', true);
      }

      spanFirst.appendChild(input);

      if (index === 1 && input.checked) {
        checked = 1;
      }
    });

    spanFirst.appendChild(switchEl);

    // Create the second 'span' wrapper
    const spanSecond = document.createElement('span');
    spanSecond.classList.add('switcher-labels');

    const labelFirst = document.createElement('span');
    labelFirst.classList.add('switcher-label-0');
    labelFirst.innerText = this.offText;

    const labelSecond = document.createElement('span');
    labelSecond.classList.add('switcher-label-1');
    labelSecond.innerText = this.onText;

    if (checked === 0) {
      labelFirst.classList.add('active');
    } else {
      labelSecond.classList.add('active');
    }

    this.spans.push(labelFirst);
    this.spans.push(labelSecond);
    spanSecond.appendChild(labelFirst);
    spanSecond.appendChild(labelSecond);

    // Append everything back to the main element
    this.appendChild(spanFirst);
    this.appendChild(spanSecond);
  }

  /** Method to toggle the switch */
  switch() {
    this.spans.forEach((span) => {
      span.classList.remove('active');
    });

    if (this.inputsContainer.classList.contains('active')) {
      this.inputsContainer.classList.remove('active');
    } else {
      this.inputsContainer.classList.add('active');
    }

    // Remove active class from all inputs
    this.inputs.forEach((input) => {
      input.classList.remove('active');
    });

    // Check if active
    if (this.newActive === 1) {
      this.inputs[this.newActive].classList.add('active');
      this.inputs[1].setAttribute('checked', '');
      this.inputs[0].removeAttribute('checked');
      this.inputsContainer.setAttribute('aria-checked', true);

      // Aria-label ONLY in the container span!
      this.inputsContainer.setAttribute('aria-label', this.spans[1].innerHTML);

      // Dispatch the "tk.switcher.on" event
      this.dispatchCustomEvent('tk.switcher.on');
    } else {
      this.inputs[1].removeAttribute('checked');
      this.inputs[0].setAttribute('checked', '');
      this.inputs[0].classList.add('active');
      this.inputsContainer.setAttribute('aria-checked', false);

      // Aria-label ONLY in the container span!
      this.inputsContainer.setAttribute('aria-label', this.spans[0].innerHTML);

      // Dispatch the "tk.switcher.off" event
      this.dispatchCustomEvent('tk.switcher.off');
    }

    this.spans[this.newActive].classList.add('active');
  }

  /** Method to toggle the switch */
  toggle(e) {
    e.preventDefault();
    this.newActive = this.inputs[1].classList.contains('active') ? 0 : 1;

    this.switch.bind(this)();
  }

  keyEvents(event) {
    if (event.keyCode === 13 || event.keyCode === 32) {
      event.preventDefault();
      this.newActive = this.inputs[1].classList.contains('active') ? 0 : 1;

      this.switch.bind(this)();
    }
  }
}

customElements.define('tk-switcher', TkSwitcherElement);
<|endoftext|>


<commit_before>// Dep: [KadOH]/rpc/object/rpc

(function(exports) {

  var KadOH     = exports,
      RPC       = KadOH.rpc.object.RPC;

  KadOH.rpc        = KadOH.rpc || {};
  KadOH.rpc.object = KadOH.rpc.object || {};

  KadOH.rpc.object.PingRPC = RPC.extend({

    initialize: function(dst_peer) {
      this.supr(dst_peer, 'PING', {});
    },

    _handleResponse: function(response) {
      this.supr(response);
    },

    _getResultFromResolve: function() {
      return this.supr();
    }

  });

})('object' === typeof module ? module.exports : (this.KadOH = this.KadOH || {}));
<commit_msg>updated ping rpc<commit_after>// Dep: [KadOH]/rpc/object/rpc

(function(exports) {

  var KadOH     = exports,
      RPC       = KadOH.rpc.object.RPC;

  KadOH.rpc        = KadOH.rpc || {};
  KadOH.rpc.object = KadOH.rpc.object || {};

  KadOH.rpc.object.PingRPC = RPC.extend({

    initialize: function(queried_peer) {
      //if no arguments, empty RPC that need to parsed from normalized query
      if(arguments.length === 0) {
        this.supr();
        return this;
      }

      this.supr(queried_peer, 'PING', {});
    },

    normalizeParams: function() {
      //nothing to do
      return {};
    },

    handleNormalizedParams: function(params) {
      //nothing to do
      this.params = null;
    },

    normalizeResult: function() {
      //nothing to do
      return {};
    },

    handleNormalizedResult: function(result) {
      //nothing to do
      this.resolve();
    }

  }).statics({

    parseQuery: function(query, quering_address) {
      var rpc = KadOH.rpc.object.PingRPC();
      rpc.handleNormalizedQuery(query, quering_address);
      return rpc;
    }
  });
})('object' === typeof module ? module.exports : (this.KadOH = this.KadOH || {}));
<|endoftext|>


<commit_before>/* global JSON */
import mobiledocParsers from '../parsers/mobiledoc';
import HTMLParser from '../parsers/html';
import TextParser from '../parsers/text';
import HTMLRenderer from 'mobiledoc-html-renderer';
import TextRenderer from 'mobiledoc-text-renderer';

const MOBILEDOC_REGEX = new RegExp(/data\-mobiledoc='(.*?)'>/);
export const MIME_TEXT_PLAIN = 'text/plain';
export const MIME_TEXT_HTML = 'text/html';

function parsePostFromHTML(html, builder, plugins) {
  let post;

  if (MOBILEDOC_REGEX.test(html)) {
    let mobiledocString = html.match(MOBILEDOC_REGEX)[1];
    let mobiledoc = JSON.parse(mobiledocString);
    post = mobiledocParsers.parse(builder, mobiledoc);
  } else {
    post = new HTMLParser(builder, {plugins}).parse(html);
  }

  return post;
}

function parsePostFromText(text, builder, plugins) {
  let parser = new TextParser(builder, {plugins});
  let post = parser.parse(text);
  return post;
}

/**
 * @param {Event} copyEvent
 * @param {Editor}
 * @return null
 */
export function setClipboardCopyData(copyEvent, editor) {
  let { range, post } = editor;
  let { clipboardData } = copyEvent;

  let mobiledoc = post.cloneRange(range);

  let unknownCardHandler = () => {}; // ignore unknown cards
  let unknownAtomHandler = () => {}; // ignore unknown atoms
  let {result: innerHTML} =
    new HTMLRenderer({unknownCardHandler, unknownAtomHandler}).render(mobiledoc);

  const html =
    `<div data-mobiledoc='${JSON.stringify(mobiledoc)}'>${innerHTML}</div>`;
  const {result: plain} =
    new TextRenderer({unknownCardHandler, unknownAtomHandler}).render(mobiledoc);

  clipboardData.setData(MIME_TEXT_PLAIN, plain);
  clipboardData.setData(MIME_TEXT_HTML, html);
}

/**
 * @param {Event} pasteEvent
 * @param {PostNodeBuilder} builder
 * @param {Array} plugins parser plugins
 * @return {Post}
 */
export function parsePostFromPaste(pasteEvent, builder, plugins=[]) {
  let post;
  let html = pasteEvent.clipboardData.getData(MIME_TEXT_HTML);

  if (!html || html.length === 0) { // Fallback to 'text/plain'
    let text = pasteEvent.clipboardData.getData(MIME_TEXT_PLAIN);
    post = parsePostFromText(text, builder, plugins);
  } else {
    post = parsePostFromHTML(html, builder, plugins);
  }

  return post;
}
<commit_msg>Fix bug #329: Copy-pasting does not work on IE11<commit_after>/* global JSON */
import mobiledocParsers from '../parsers/mobiledoc';
import HTMLParser from '../parsers/html';
import TextParser from '../parsers/text';
import HTMLRenderer from 'mobiledoc-html-renderer';
import TextRenderer from 'mobiledoc-text-renderer';

const MOBILEDOC_REGEX = new RegExp(/data\-mobiledoc='(.*?)'>/);
export const MIME_TEXT_PLAIN = 'text/plain';
export const MIME_TEXT_HTML = 'text/html';

function parsePostFromHTML(html, builder, plugins) {
  let post;

  if (MOBILEDOC_REGEX.test(html)) {
    let mobiledocString = html.match(MOBILEDOC_REGEX)[1];
    let mobiledoc = JSON.parse(mobiledocString);
    post = mobiledocParsers.parse(builder, mobiledoc);
  } else {
    post = new HTMLParser(builder, {plugins}).parse(html);
  }

  return post;
}

function parsePostFromText(text, builder, plugins) {
  let parser = new TextParser(builder, {plugins});
  let post = parser.parse(text);
  return post;
}

/**
 * @param {Event} copyEvent
 * @param {Editor}
 * @return null
 */
export function setClipboardCopyData(copyEvent, editor) {
  let { range, post } = editor;
  let { clipboardData } = copyEvent;

  let mobiledoc = post.cloneRange(range);

  let unknownCardHandler = () => {}; // ignore unknown cards
  let unknownAtomHandler = () => {}; // ignore unknown atoms
  let {result: innerHTML} =
    new HTMLRenderer({unknownCardHandler, unknownAtomHandler}).render(mobiledoc);

  const html =
    `<div data-mobiledoc='${JSON.stringify(mobiledoc)}'>${innerHTML}</div>`;
  const {result: plain} =
    new TextRenderer({unknownCardHandler, unknownAtomHandler}).render(mobiledoc);

  if (clipboardData && clipboardData.setData) {
    clipboardData.setData(MIME_TEXT_PLAIN, plain);
    clipboardData.setData(MIME_TEXT_HTML, html);
  } else if (window.clipboardData && window.clipboardData.setData) { // IE
    window.clipboardData.setData('Text', html);
  }
}

/**
 * @param {Event} pasteEvent
 * @param {PostNodeBuilder} builder
 * @param {Array} plugins parser plugins
 * @return {Post}
 */
export function parsePostFromPaste(pasteEvent, builder, plugins=[]) {
  let post;
  let html;
  let text;

  if (pasteEvent.clipboardData && pasteEvent.clipboardData.getData) {
    html = pasteEvent.clipboardData.getData(MIME_TEXT_HTML);

    if (!html || html.length === 0) { // Fallback to 'text/plain'
      text = pasteEvent.clipboardData.getData(MIME_TEXT_PLAIN);
    }
  } else if (window.clipboardData && window.clipboardData.getData) { // IE
    html = window.clipboardData.getData('Text');
  }

  if (html && html.length > 0) {
    post = parsePostFromHTML(html, builder, plugins);
  } else if (text && text.length > 0) {
    post = parsePostFromText(text, builder, plugins);
  }

  return post;
}
<|endoftext|>


<commit_before>'use strict';
goog.provide('banner_html');

goog.require('banner_utils');
goog.require('utils');
goog.require('session');
goog.require('storage'); // jshint unused:false

/**
 * @param {banner_utils.options} options
 * @param {string} action
 */
banner_html.banner = function(options, action) {
	return '
' +
		'
' +
			action +
		'
' +
		'
' +
			(!options.disableHide ?
				'
×
' :
				'') +
			'
' +
				'' +
			'
' +
			'
' +
				'
' + options.title + '
' +
				((options.rating || options.reviewCount) ?
					('
' +
						(options.rating ?
							('' +
								(function() {
									var stars = "";
									for (var i = 0; i < 5; i++) {
										stars += '' +
												'<svg class="star" fill="#555555" height="12" viewBox="3 2 20 19" width="12">' +
												'<path d="M22 9.24l-7.19-.62L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21 12 17.27 18.18 21l-1.63-7.03L22 9.24zM12 15.4l-3.76 2.27 1-4.28-3.32-2.88 4.38-.38L12 6.1l1.71 4.04 4.38.38-3.32 2.88 1 4.28L12 15.4z"/>' +
												'<path d="M0 0h24v24H0z" fill="none"/>' +
												'<foreignObject display="none">' +
												'☆' +
												'</foreignObject>' +
												'</svg>';
										if (options.rating > i) {
											stars += (i + 1 > options.rating && options.rating % 1) ?
												'' +
													'<svg fill="#555555" height="12" viewBox="3 2 20 19" width="12">' +
													'<defs>' +
													'<path d="M0 0h24v24H0V0z" id="a"/>' +
													'</defs>' +
													'<clipPath id="b">' +
													'<use overflow="visible" xlink:href="#a"/>' +
													'</clipPath>' +
													'<path clip-path="url(#b)" d="M22 9.24l-7.19-.62L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21 12 17.27 18.18 21l-1.63-7.03L22 9.24zM12 15.4V6.1l1.71 4.04 4.38.38-3.32 2.88 1 4.28L12 15.4z"/>' +
													'</svg>' +
													'<foreignObject display="none">' +
													'★' +
													'</foreignObject>' +
													'' :
												'' +
													'<svg fill="#555555" height="12" viewBox="3 2 20 19" width="12">' +
													'<path d="M0 0h24v24H0z" fill="none"/>' +
													'<path d="M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z"/>' +
													'<path d="M0 0h24v24H0z" fill="none"/>' +
													'<foreignObject display="none">' +
													'★' +
													'</foreignObject>' +
													'</svg>' +
													' ';
										}
										stars += '';
									}
									return stars;
								})() +
							'') :
							'') +
						(options.reviewCount ?
							('' + options.reviewCount + '') :
							'') +
					'
') :
					'') +
				'
' + options.description + '
' +
			'
' +
		'
' +
	'
';
};

/**
 * @param {banner_utils.options} options
 * @param {storage} storage
 */
banner_html.mobileAction = function(options, storage) {
	return '' +
		((session.get(storage) || {})['has_app'] ?
			options.openAppButtonText :
			options.downloadAppButtonText) +
		'';
};

banner_html.desktopAction = function(options) {
	return '
' +
		'

' +
	'
' +
	'
' +
		'<form id="sms-form">' +
			'<input type="phone" aria-label="Enter phone number" class="branch-animation" name="branch-sms-phone"' +
			' id="branch-sms-phone" placeholder="' +
			options.phonePreviewText +
			'">' +
			'<button type="submit" id="branch-sms-send" class="branch-animation button">' +
			options.sendLinkText + '</button>' +
		'</form>' +
	'
';
};

banner_html.checkmark = function() {
	if (window.ActiveXObject) {
		return '✓';
	}
	else {
		return '<svg version="1.1" id="Layer_1" x="0px" y="0px" viewBox="0 0 98.5 98.5"' +
			' enable-background="new 0 0 98.5 98.5" xml:space="preserve">' +
			'<path class="checkmark" fill="none" stroke-width="8" stroke-miterlimit="10"' +
			' d="M81.7,17.8C73.5,9.3,62,4,49.2,4' +
			'C24.3,4,4,24.3,4,49.2s20.3,45.2,45.2,45.2s45.2-20.3,' +
			'45.2-45.2c0-8.6-2.4-16.6-6.5-23.4l0,0L45.6,68.2L24.7,47.3"/>' +
		'</svg>';
	}
};

/**
 * @param {banner_utils.options} options
 */
banner_html.iframe = function(options, action) {
	var iframe = document.createElement('iframe');
	iframe.src = 'about:blank'; // solves CORS issues, test in IE
	iframe.style.overflow = 'hidden';
	iframe.scrolling = 'no';
	iframe.id = 'branch-banner-iframe';
	iframe.className = 'branch-animation';
	utils.addNonceAttribute(iframe);

	document.body.appendChild(iframe);

	var bodyClass;
	var userAgent = utils.mobileUserAgent();
	if (userAgent === 'ios' || userAgent === 'ipad') {
		bodyClass = 'branch-banner-ios';
	}
	else if (userAgent === 'android') {
		bodyClass = 'branch-banner-android';
	}
	else {
		bodyClass = 'branch-banner-desktop';
	}

	var iframeHTML = '
' +
		banner_html.banner(options, action) +
		'';
	iframe.contentWindow.document.open();
	iframe.contentWindow.document.write(iframeHTML);
	iframe.contentWindow.document.close();

	return iframe;
};

/**
 * @param {banner_utils.options} options
 */
banner_html.div = function(options, action) {
	var banner = document.createElement('div');
	banner.id = 'branch-banner';
	banner.className = 'branch-animation';
	banner.innerHTML = banner_html.banner(options, action);
	document.body.appendChild(banner);

	return banner;
};

/**
 * @param {banner_utils.options} options
 * @param {storage} storage
 */
banner_html.markup = function(options, storage) {
	var action = '
' +
		(utils.mobileUserAgent() ?
			banner_html.mobileAction(options, storage) :
			banner_html.desktopAction(options)) +
		'
';

	if (options.iframe) {
		return banner_html.iframe(options, action);
	}
	else {
		return banner_html.div(options, action);
	}
};
<commit_msg>Also eliminate document.write() from branch.banner() calls.<commit_after>'use strict';
goog.provide('banner_html');

goog.require('banner_utils');
goog.require('utils');
goog.require('session');
goog.require('storage'); // jshint unused:false

/**
 * @param {banner_utils.options} options
 * @param {string} action
 */
banner_html.banner = function(options, action) {
	return '
' +
		'
' +
			action +
		'
' +
		'
' +
			(!options.disableHide ?
				'
×
' :
				'') +
			'
' +
				'' +
			'
' +
			'
' +
				'
' + options.title + '
' +
				((options.rating || options.reviewCount) ?
					('
' +
						(options.rating ?
							('' +
								(function() {
									var stars = "";
									for (var i = 0; i < 5; i++) {
										stars += '' +
												'<svg class="star" fill="#555555" height="12" viewBox="3 2 20 19" width="12">' +
												'<path d="M22 9.24l-7.19-.62L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21 12 17.27 18.18 21l-1.63-7.03L22 9.24zM12 15.4l-3.76 2.27 1-4.28-3.32-2.88 4.38-.38L12 6.1l1.71 4.04 4.38.38-3.32 2.88 1 4.28L12 15.4z"/>' +
												'<path d="M0 0h24v24H0z" fill="none"/>' +
												'<foreignObject display="none">' +
												'☆' +
												'</foreignObject>' +
												'</svg>';
										if (options.rating > i) {
											stars += (i + 1 > options.rating && options.rating % 1) ?
												'' +
													'<svg fill="#555555" height="12" viewBox="3 2 20 19" width="12">' +
													'<defs>' +
													'<path d="M0 0h24v24H0V0z" id="a"/>' +
													'</defs>' +
													'<clipPath id="b">' +
													'<use overflow="visible" xlink:href="#a"/>' +
													'</clipPath>' +
													'<path clip-path="url(#b)" d="M22 9.24l-7.19-.62L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21 12 17.27 18.18 21l-1.63-7.03L22 9.24zM12 15.4V6.1l1.71 4.04 4.38.38-3.32 2.88 1 4.28L12 15.4z"/>' +
													'</svg>' +
													'<foreignObject display="none">' +
													'★' +
													'</foreignObject>' +
													'' :
												'' +
													'<svg fill="#555555" height="12" viewBox="3 2 20 19" width="12">' +
													'<path d="M0 0h24v24H0z" fill="none"/>' +
													'<path d="M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z"/>' +
													'<path d="M0 0h24v24H0z" fill="none"/>' +
													'<foreignObject display="none">' +
													'★' +
													'</foreignObject>' +
													'</svg>' +
													' ';
										}
										stars += '';
									}
									return stars;
								})() +
							'') :
							'') +
						(options.reviewCount ?
							('' + options.reviewCount + '') :
							'') +
					'
') :
					'') +
				'
' + options.description + '
' +
			'
' +
		'
' +
	'
';
};

/**
 * @param {banner_utils.options} options
 * @param {storage} storage
 */
banner_html.mobileAction = function(options, storage) {
	return '' +
		((session.get(storage) || {})['has_app'] ?
			options.openAppButtonText :
			options.downloadAppButtonText) +
		'';
};

banner_html.desktopAction = function(options) {
	return '
' +
		'

' +
	'
' +
	'
' +
		'<form id="sms-form">' +
			'<input type="phone" aria-label="Enter phone number" class="branch-animation" name="branch-sms-phone"' +
			' id="branch-sms-phone" placeholder="' +
			options.phonePreviewText +
			'">' +
			'<button type="submit" id="branch-sms-send" class="branch-animation button">' +
			options.sendLinkText + '</button>' +
		'</form>' +
	'
';
};

banner_html.checkmark = function() {
	if (window.ActiveXObject) {
		return '✓';
	}
	else {
		return '<svg version="1.1" id="Layer_1" x="0px" y="0px" viewBox="0 0 98.5 98.5"' +
			' enable-background="new 0 0 98.5 98.5" xml:space="preserve">' +
			'<path class="checkmark" fill="none" stroke-width="8" stroke-miterlimit="10"' +
			' d="M81.7,17.8C73.5,9.3,62,4,49.2,4' +
			'C24.3,4,4,24.3,4,49.2s20.3,45.2,45.2,45.2s45.2-20.3,' +
			'45.2-45.2c0-8.6-2.4-16.6-6.5-23.4l0,0L45.6,68.2L24.7,47.3"/>' +
		'</svg>';
	}
};

/**
 * @param {banner_utils.options} options
 */
banner_html.iframe = function(options, action) {
	var iframe = document.createElement('iframe');
	iframe.src = 'about:blank'; // solves CORS issues, test in IE
	iframe.style.overflow = 'hidden';
	iframe.scrolling = 'no';
	iframe.id = 'branch-banner-iframe';
	iframe.className = 'branch-animation';
	utils.addNonceAttribute(iframe);

	document.body.appendChild(iframe);

	var bodyClass;
	var userAgent = utils.mobileUserAgent();
	if (userAgent === 'ios' || userAgent === 'ipad') {
		bodyClass = 'branch-banner-ios';
	}
	else if (userAgent === 'android') {
		bodyClass = 'branch-banner-android';
	}
	else {
		bodyClass = 'branch-banner-desktop';
	}

	var html = banner_html.banner(options, action);
	var iframedoc = iframe.contentDocument || iframe.contentWindow.document;
	iframedoc.head = iframedoc.createElement('head');
	iframedoc.body = iframedoc.createElement('body');
	iframedoc.body.innerHTML = html;
	iframedoc.body.className = bodyClass;

	return iframe;
};

/**
 * @param {banner_utils.options} options
 */
banner_html.div = function(options, action) {
	var banner = document.createElement('div');
	banner.id = 'branch-banner';
	banner.className = 'branch-animation';
	banner.innerHTML = banner_html.banner(options, action);
	document.body.appendChild(banner);

	return banner;
};

/**
 * @param {banner_utils.options} options
 * @param {storage} storage
 */
banner_html.markup = function(options, storage) {
	var action = '
' +
		(utils.mobileUserAgent() ?
			banner_html.mobileAction(options, storage) :
			banner_html.desktopAction(options)) +
		'
';

	if (options.iframe) {
		return banner_html.iframe(options, action);
	}
	else {
		return banner_html.div(options, action);
	}
};
<|endoftext|>


<commit_before>export default PromiseLauncher;

// Create a command launcher with `errorHandler` and `options`.
function PromiseLauncher(errorHandler, options) {
  this.errorHandler = errorHandler;
  this.options = options || {};
}

PromiseLauncher.prototype = {
  // Launch the execution of `Command` function. It create the command and wraps it in a Promise.
  execute: function(Command, args) {
    var resolver,
      _this = this;

    // create the resolver responsible for the creation and the execution
    // of the `Command`.
    // The command created must conform to the API `Command(resolve, reject)`.
    resolver = function(resolve, reject) {
      var command = new Command(resolve, reject);
      return command.execute.apply(command, args);
    };
    // return the created promise
    return this.promise(resolver).catch(this.errorHandler);
  },

  // This function is the one responsible for creating the promise around the `resolver` provided .
  // The purpose of this function is to permit overriding of the promise definition based on any logic or context.
  promise: function (resolver) {
    // create the promise based on the promise function given
    // as an option
    var Promise = this.options.promise || this.Promise;
    return new Promise(resolver);
  }
};
<commit_msg>fix(promiseLauncher): fix Promise fallback<commit_after>export default PromiseLauncher;

// Create a command launcher with `errorHandler` and `options`.
function PromiseLauncher(errorHandler, options) {
  this.errorHandler = errorHandler;
  this.options = options || {};
}

PromiseLauncher.prototype = {
  // Launch the execution of `Command` function. It create the command and wraps it in a Promise.
  execute: function(Command, args) {
    var resolver,
      _this = this;

    // create the resolver responsible for the creation and the execution
    // of the `Command`.
    // The command created must conform to the API `Command(resolve, reject)`.
    resolver = function(resolve, reject) {
      var command = new Command(resolve, reject);
      return command.execute.apply(command, args);
    };
    // return the created promise
    return this.promise(resolver).catch(this.errorHandler);
  },

  // This function is the one responsible for creating the promise around the `resolver` provided .
  // The purpose of this function is to permit overriding of the promise definition based on any logic or context.
  promise: function (resolver) {
    // create the promise based on the promise function given
    // as an option
    var PromiseConstructor = this.options.promise || Promise;
    return new PromiseConstructor(resolver);
  }
};
<|endoftext|>


<commit_before>import PropTypes from 'prop-types';
import React, { Component } from 'react';
import isFunction from 'lodash/isFunction';
import isNil from 'lodash/isNil';
import composeValidators from '../utils/compose-validators';
import assertAddRemoveProp from '../assert/assertAddRemoveProp';
import assertChangeProp from '../assert/assertChangeProp';
import assertType from '../assert/assertType';
import { SHARE } from '../utils/const';
import { consoleLog, consoleTime, consoleTimeEnd } from '../utils/log';
import Mediator from '../mediator/Mediator';
import { change, reset, initialize, submit } from '../utils/global';
import PrimitiveEE from '../utils/PrimitiveEE';
//import KeyComponent from './KeyComponent';

let counter = 0;
const salt = Date.now();

export default class FieldComponent extends Component {
  static propTypes = {
    id: PropTypes.oneOfType([ PropTypes.string, PropTypes.number ]),
    name: PropTypes.oneOfType([ PropTypes.string, PropTypes.number ]),
    form: PropTypes.bool,
    autoClean: PropTypes.bool,

    postponeInvalid: PropTypes.bool,

    value: PropTypes.any,
    onChange: PropTypes.func,
    onValueChange: PropTypes.func,

    onChangeValidity: PropTypes.func,

    //meta: PropTypes.object, // TODO: implement meta store
    //onChangeMeta: PropTypes.func, // TODO: implement meta store

    validate: PropTypes.oneOfType([ PropTypes.arrayOf(PropTypes.func), PropTypes.func ]),

    initialValue: PropTypes.any,

    onSubmit: PropTypes.func,

    exceptionMessage: PropTypes.any,

    share: PropTypes.oneOf(Object.values(SHARE)),
    debug: PropTypes.bool,

    children: PropTypes.func.isRequired
  };

  static defaultProps = {
    autoClean: false,
    name: null,
    exceptionMessage: 'Invalid Value',
    validate: null,
    share: null,
    initialValue: undefined,
    debug: false,
    form: false,
    onSubmit: null,
    onChange: null
  };

  static childContextTypes = {
    _fractalFieldMediator: PropTypes.object
  };

  static contextTypes = {
    _fractalFieldMediator: PropTypes.object
  };

  getValue () {
    if (this._valueIsPostponed) {
      return this._postponedValue;
    }

    if (this.provideValueStorage) {
      return this.props.value;
    }

    return this.getState().value;
  }

  getMeta () {
    return this.getState().meta;
  }

  getClothesIsolatedMediatorMeta () {
    return this.mediator.getClothesIsolatedMediator().getState().meta;
  }

  getValueType () {
    throw new Error('getType must be overraded!');
  }

  getChildContext () {
    return {
      _fractalFieldMediator: this.mediator
    };
  }

  getInitialParams () {
    return {
      type: this.valueType,
      name: this.props.name,
      share: this.share,
      value: this.getInitialValue(),
      autoClean: Boolean(this.props.autoClean)
    };
  }

  getInitialValue () {
    let value = this.provideValueStorage ? (this.props.value === undefined ? this.props.initialValue : this.props.value) : this.props.initialValue;

    if (this.hasParentForm && this.props.name !== null) {
      const tmpVal = this.context._fractalFieldMediator.getState().value;

      value = tmpVal && tmpVal[this.props.name] !== undefined ? tmpVal[this.props.name] : value;
    }

    return value;
  }

  constructor (...args) {
    super(...args);

    this.state = {
      form: {},
      postponedValue: undefined
    };

    this.valueType = this.getValueType();

    this.provideValueStorage = this.props.hasOwnProperty('value') && this.props.hasOwnProperty('onChange');
    this.provideMetaStorage = this.props.hasOwnProperty('meta') && this.props.hasOwnProperty('onChangeMeta');

    this.componentID = this.props.id || `fractal-field-${salt}-${++counter}-${this.props.name == null ? (this.hasParentForm ? '@' : '#') : this.props.name}`;

    this.hasParentForm = Boolean(this.context._fractalFieldMediator);

    this.isForm = !this.hasParentForm || this.props.form;

    if (this.provideValueStorage && this.provideMetaStorage || !this.hasParentForm) {
      this.share = SHARE.NONE;
    } else if (this.provideValueStorage || this.props.name === null) {
      this.share = SHARE.META;
    } else if (this.provideMetaStorage) {
      this.share = SHARE.VALUE;
    } else if (this.props.share) {
      this.share = this.props.share;
    } else {
      this.share = SHARE.ALL;
    }

    if (process.env.NODE_ENV !== 'production') {
      if (this.props.share && this.props.share !== this.share) {
        throw new Error(`invalid sharing type "${this.props.share}" specified in props [it will be ignored in production mode]. you should not manually define the share prop in this case`);
      }
    }

    this.debug = this.context._fractalFieldMediator ? this.context._fractalFieldMediator._debug : this.props.debug;

    this.mediator = new Mediator(this.componentID, this.share === SHARE.NONE ? null : this.context._fractalFieldMediator, { debug: this.debug });

    this.flushInitializeTransaction = this.mediator.transaction('init');

    this.mediator.mount(this.getInitialParams());

    this._paramsVersion = null;
    this._prevParams = null;

    this._notifiedChangeValue = undefined;

    this._renderedVersion = 0;

    this._initialValueVersionVersion = 0;

    this._prevValidatedValue = -1;
    this._prevValidate = null;

    this._postponedValue = undefined;
    this._valueIsPostponed = false;

    this.render = this.render.bind(this);
    this.triggerReset = this.triggerReset.bind(this);
    this.triggerChange = this.triggerChange.bind(this);
    this.triggerSubmit = this.triggerSubmit.bind(this);
    this.renderChildren = this.renderChildren.bind(this);

    this._destroy$ = PrimitiveEE();
  }

  onDestroy (listener) {
    return this._destroy$.on(listener);
  }

  getState () {
    return this.mediator.getState();
  }

  notifyOnChange (value) {
    if (!this.props.onChange || this._notifiedChangeValue === value) {
      return;
    }

    this._notifiedChangeValue = value;

    this.props.onChange(value);
  }

  _notifyValueOnChange (value) {
    if (!this.props.onValueChange || this._notifiedValueChangeValue === value) {
      return;
    }

    this._notifiedValueChangeValue = value;

    this.props.onValueChange(value);
  }

  _updateComponentState (values) {
    this.setState((prevState) => ({ ...prevState, ...values })); // eslint-disable-line react/no-set-state
  }

  triggerChange (normalizedValue) {
    if (this.props.postponeInvalid) {
      const isValid = this.validate(normalizedValue) && !this.getMeta().hasException;

      this._valueIsPostponed = !isValid;

      if (this._valueIsPostponed) {
        this._postponedValue = normalizedValue;
        this._updateComponentState({ postponedValue: normalizedValue });
        return;
      }
    }

    if (!this.provideValueStorage) {
      this.mediator.changeValue(normalizedValue);

      normalizedValue = this.getValue();
    }

    this.notifyOnChange(normalizedValue);
  }

  triggerReset (initialValue = this.getInitialValue()) {
    this.mediator.reset(initialValue);
  }

  triggerSubmit (ev) {
    if (ev) {
      ev.preventDefault();
    }

    const flush = this.mediator.transaction('triggerSubmit');

    this.mediator.submitStart();

    if (!this.getMeta().valid) {
      this.mediator.submit();
      flush();
      return Promise.reject(null); // eslint-disable-line prefer-promise-reject-errors
    }

    if (!this.props.onSubmit) {
      this.mediator.submitDone();
      flush();
      return Promise.reject(null); // eslint-disable-line prefer-promise-reject-errors
    }

    const value = this.getValue();

    flush();

    return new Promise(() => {
      return Promise.resolve(this.props.onSubmit(value));
    }).then(
      () => {
        this.mediator.submitDone();
      },
      (err) => {
        this.mediator.submitFailed(err);

        return Promise.reject(err);
      }
    );
  }

  validate (value) {
    const { validate } = this.props;
    const flush = this.mediator.transaction('validate');

    const validateIsArray = Array.isArray(validate);

    if (!validate || (validateIsArray && !validate.length)) {
      this.mediator.changeValidity(null);
      flush();
      return true;
    }

    if (this._prevValidate === validate && value === this._prevValidatedValue) {
      flush();

      return !this.getMeta().error;
    }

    let error = null;
    const _validate = validateIsArray ? composeValidators(...validate) : validate;

    try {
      error = _validate(value);
      this._prevValidate = validate;
      this._prevValidatedValue = value;
    } catch (err) {
      this.mediator.sysValidateError(err);
      flush();
      return false;
    }

    this.mediator.sysValidateError(null);
    this.mediator.changeValidity(error);

    flush();

    return !error;
  }

  calcParams () {
    const state = this.getState();

    const meta = this.getMeta();

    const formFieldMeta = this.getClothesIsolatedMediatorMeta();

    let params = {
      triggerChange: this.triggerChange,
      triggerReset: this.triggerReset,

      valid: meta.valid,
      invalid: !meta.valid,
      hasException: meta.hasException,
      error: meta.hasException ? this.props.exceptionMessage : meta.error,
      invalidChildren: this.mediator.dehydrateNames(meta.errorsIn).filter(Boolean),

      value: this.getValue(),

      pristine: !meta.touched,
      touched: meta.touched,

      active: meta.active,
      activated: meta.activated,

      form: {
        valid: formFieldMeta.valid,
        invalid: !formFieldMeta.valid,
        hasException: formFieldMeta.hasException,

        active: formFieldMeta.active,
        activated: formFieldMeta.activated,

        touched: formFieldMeta.touched,
        pristine: !formFieldMeta.touched,

        submitted: formFieldMeta.submitted,
        submittedTimes: formFieldMeta.submittedTimes,
        submitFailed: formFieldMeta.submitFailed,
        submitSuccess: formFieldMeta.submitSuccess,
        submitting: formFieldMeta.submitting
      }
    };

    if (this.isForm) {
      params = {
        ...params,
        triggerSubmit: this.triggerSubmit,
        submitted: meta.submitted,
        submittedTimes: meta.submittedTimes,
        submitFailed: meta.submitFailed,
        submitSuccess: meta.submitSuccess,
        submitting: meta.submitting
      };
    }

    if (this.debug) {
      params.$field = state;
      params.$state = this.mediator._store._state;
    }

    return params;
  }

  _calcParams () {
    if (this._paramsVersion === this.mediator.version()) {
      return this._prevParams;
    }

    this._paramsVersion = this.mediator.version();

    const params = this.calcParams();

    this._prevParams = params;

    return params;
  }

  renderChildren () {
    const { children } = this.props;

    this._renderedVersion = this.mediator.version();

    return isFunction(children) ? children(this._calcParams()) : children;
  }

  renderInner () {
    return this.renderChildren();
  }

  render () {
    return this.renderInner();
  }

  subscribeGlobalStreams () {
    this.onDestroy(change.subscribe(this.componentID, (componentID, value) => this.triggerChange(value)));

    this.onDestroy(reset.subscribe(this.componentID, (initialValue) => this.triggerReset(initialValue)));

    this.onDestroy(initialize.subscribe(this.componentID, () => {
      // TODO: trigger initialize
    }));

    this.onDestroy(submit.subscribe(this.componentID, () => this.triggerSubmit()));
  }

  _refreshIfChanged () {
    const currentVersion = this.mediator.version();

    if (this.share !== SHARE.NONE || this._prevVersion === currentVersion) {
      return;
    }

    this._prevVersion = currentVersion;

    if (this.debug) {
      consoleLog(`TRIGGER SET STATE ${this.componentID}`);
      consoleTime(`RERENDERED ${this.componentID}`);
    }

    this._updateComponentState({ form: this.mediator.getFullState() });
  }

  _unwatchMediatorChange () {}

  _watchMediatorChange () {
    this._unwatchMediatorChange();

    this._unwatchMediatorChange = this.mediator.onceChange(() => {
      this._refreshIfChanged();
    });
  }

  componentWillMount () {
  }

  componentDidMount () {
    //console.log('INIT', this.componentID);
    this.mediator.init();
    this.validate(this.getValue());
    this.flushInitializeTransaction();
    this.subscribeGlobalStreams();
    this._refreshIfChanged();
    this._watchMediatorChange();
  }

  componentWillUnmount () {
    this._destroy$.emit();
    this._destroy$.destroy();
    this.mediator.destroy();
  }

  componentDidUpdate (prevProps, prevState) {
    this._unwatchMediatorChange();

    if (this.debug && this.share === SHARE.NONE) {
      consoleTimeEnd(`RERENDERED ${this.componentID}`);
    }

    const flush = this.mediator.transaction('componentDidUpdate');

    const valueVersion = this.getState().valueVersion;

    if (this._valueIsPostponed && this._prevValueVersion !== valueVersion) {
      this._prevValueVersion = valueVersion;
      this._valueIsPostponed = false;
    }

    this.onUpdate(prevProps, prevState);

    const meta = this.getMeta();

    if (this.props.onChangeValidity && this._prevValidity !== meta.valid) {
      this.props.onChangeValidity(meta.valid);
    }

    this._prevValidity = meta.valid;

    flush();

    this._refreshIfChanged();

    this._watchMediatorChange();
  }

  onUpdate (prevProps) {
    const val = this.getValue();

    if (process.env.NODE_ENV !== 'production') {
      assertChangeProp(this.props, prevProps, 'id');
      assertChangeProp(this.props, prevProps, 'share');
      assertChangeProp(this.props, prevProps, 'form');
      assertChangeProp(this.props, prevProps, 'debug');
      assertChangeProp(this.props, prevProps, 'autoClean');
      assertAddRemoveProp(this.props, prevProps, 'value');
      assertAddRemoveProp(this.props, prevProps, 'name');
      assertAddRemoveProp(this.props, prevProps, 'debug');
      assertAddRemoveProp(this.props, prevProps, 'onChange');
      assertAddRemoveProp(this.props, prevProps, 'onChangeMeta');
      assertAddRemoveProp(this.props, prevProps, 'meta');
      assertAddRemoveProp(this.props, prevProps, 'form');
      assertAddRemoveProp(this.props, prevProps, 'share');
      assertAddRemoveProp(this.props, prevProps, 'id');
      assertAddRemoveProp(this.props, prevProps, 'autoClean');

      if (!isNil(val)) {
        assertType(`value of field "${this.componentID}" or null/undefined`, this.valueType, val);
      }
    }

    if (this.props.name !== prevProps.name) {
      this.mediator.rename(this.props.name);
    }

    this._notifyValueOnChange(val);

    this.validate(val);
  }
}
<commit_msg>fix params recalculation when postponed value are updated<commit_after>import PropTypes from 'prop-types';
import React, { Component } from 'react';
import isFunction from 'lodash/isFunction';
import isNil from 'lodash/isNil';
import composeValidators from '../utils/compose-validators';
import assertAddRemoveProp from '../assert/assertAddRemoveProp';
import assertChangeProp from '../assert/assertChangeProp';
import assertType from '../assert/assertType';
import { SHARE } from '../utils/const';
import { consoleLog, consoleTime, consoleTimeEnd } from '../utils/log';
import Mediator from '../mediator/Mediator';
import { change, reset, initialize, submit } from '../utils/global';
import PrimitiveEE from '../utils/PrimitiveEE';
//import KeyComponent from './KeyComponent';

let counter = 0;
const salt = Date.now();

export default class FieldComponent extends Component {
  static propTypes = {
    id: PropTypes.oneOfType([ PropTypes.string, PropTypes.number ]),
    name: PropTypes.oneOfType([ PropTypes.string, PropTypes.number ]),
    form: PropTypes.bool,
    autoClean: PropTypes.bool,

    postponeInvalid: PropTypes.bool,

    value: PropTypes.any,
    onChange: PropTypes.func,
    onValueChange: PropTypes.func,

    onChangeValidity: PropTypes.func,

    //meta: PropTypes.object, // TODO: implement meta store
    //onChangeMeta: PropTypes.func, // TODO: implement meta store

    validate: PropTypes.oneOfType([ PropTypes.arrayOf(PropTypes.func), PropTypes.func ]),

    initialValue: PropTypes.any,

    onSubmit: PropTypes.func,

    exceptionMessage: PropTypes.any,

    share: PropTypes.oneOf(Object.values(SHARE)),
    debug: PropTypes.bool,

    children: PropTypes.func.isRequired
  };

  static defaultProps = {
    autoClean: false,
    name: null,
    exceptionMessage: 'Invalid Value',
    validate: null,
    share: null,
    initialValue: undefined,
    debug: false,
    form: false,
    onSubmit: null,
    onChange: null
  };

  static childContextTypes = {
    _fractalFieldMediator: PropTypes.object
  };

  static contextTypes = {
    _fractalFieldMediator: PropTypes.object
  };

  getValue () {
    if (this._valueIsPostponed) {
      return this._postponedValue;
    }

    if (this.provideValueStorage) {
      return this.props.value;
    }

    return this.getState().value;
  }

  getMeta () {
    return this.getState().meta;
  }

  getClothesIsolatedMediatorMeta () {
    return this.mediator.getClothesIsolatedMediator().getState().meta;
  }

  getValueType () {
    throw new Error('getType must be overraded!');
  }

  getChildContext () {
    return {
      _fractalFieldMediator: this.mediator
    };
  }

  getInitialParams () {
    return {
      type: this.valueType,
      name: this.props.name,
      share: this.share,
      value: this.getInitialValue(),
      autoClean: Boolean(this.props.autoClean)
    };
  }

  getInitialValue () {
    let value = this.provideValueStorage ? (this.props.value === undefined ? this.props.initialValue : this.props.value) : this.props.initialValue;

    if (this.hasParentForm && this.props.name !== null) {
      const tmpVal = this.context._fractalFieldMediator.getState().value;

      value = tmpVal && tmpVal[this.props.name] !== undefined ? tmpVal[this.props.name] : value;
    }

    return value;
  }

  constructor (...args) {
    super(...args);

    this.state = {
      form: {},
      postponedValue: undefined
    };

    this.valueType = this.getValueType();

    this.provideValueStorage = this.props.hasOwnProperty('value') && this.props.hasOwnProperty('onChange');
    this.provideMetaStorage = this.props.hasOwnProperty('meta') && this.props.hasOwnProperty('onChangeMeta');

    this.componentID = this.props.id || `fractal-field-${salt}-${++counter}-${this.props.name == null ? (this.hasParentForm ? '@' : '#') : this.props.name}`;

    this.hasParentForm = Boolean(this.context._fractalFieldMediator);

    this.isForm = !this.hasParentForm || this.props.form;

    if (this.provideValueStorage && this.provideMetaStorage || !this.hasParentForm) {
      this.share = SHARE.NONE;
    } else if (this.provideValueStorage || this.props.name === null) {
      this.share = SHARE.META;
    } else if (this.provideMetaStorage) {
      this.share = SHARE.VALUE;
    } else if (this.props.share) {
      this.share = this.props.share;
    } else {
      this.share = SHARE.ALL;
    }

    if (process.env.NODE_ENV !== 'production') {
      if (this.props.share && this.props.share !== this.share) {
        throw new Error(`invalid sharing type "${this.props.share}" specified in props [it will be ignored in production mode]. you should not manually define the share prop in this case`);
      }
    }

    this.debug = this.context._fractalFieldMediator ? this.context._fractalFieldMediator._debug : this.props.debug;

    this.mediator = new Mediator(this.componentID, this.share === SHARE.NONE ? null : this.context._fractalFieldMediator, { debug: this.debug });

    this.flushInitializeTransaction = this.mediator.transaction('init');

    this.mediator.mount(this.getInitialParams());

    this._paramsVersion = null;
    this._prevParams = null;

    this._notifiedChangeValue = undefined;

    this._renderedVersion = 0;

    this._initialValueVersionVersion = 0;

    this._prevValidatedValue = -1;
    this._prevValidate = null;

    this._postponedValue = undefined;
    this._prevPostponedValue = undefined;
    this._valueIsPostponed = false;

    this.render = this.render.bind(this);
    this.triggerReset = this.triggerReset.bind(this);
    this.triggerChange = this.triggerChange.bind(this);
    this.triggerSubmit = this.triggerSubmit.bind(this);
    this.renderChildren = this.renderChildren.bind(this);

    this._destroy$ = PrimitiveEE();
  }

  onDestroy (listener) {
    return this._destroy$.on(listener);
  }

  getState () {
    return this.mediator.getState();
  }

  notifyOnChange (value) {
    if (!this.props.onChange || this._notifiedChangeValue === value) {
      return;
    }

    this._notifiedChangeValue = value;

    this.props.onChange(value);
  }

  _notifyValueOnChange (value) {
    if (!this.props.onValueChange || this._notifiedValueChangeValue === value) {
      return;
    }

    this._notifiedValueChangeValue = value;

    this.props.onValueChange(value);
  }

  _updateComponentState (values) {
    this.setState((prevState) => ({ ...prevState, ...values })); // eslint-disable-line react/no-set-state
  }

  triggerChange (normalizedValue) {
    if (this.props.postponeInvalid) {
      const isValid = this.validate(normalizedValue) && !this.getMeta().hasException;

      this._valueIsPostponed = !isValid;

      if (this._valueIsPostponed) {
        this._postponedValue = normalizedValue;
        this._updateComponentState({
          postponedValue: this._postponedValue
        });
        return;
      }
    }

    if (!this.provideValueStorage) {
      this.mediator.changeValue(normalizedValue);

      normalizedValue = this.getValue();
    }

    this.notifyOnChange(normalizedValue);
  }

  triggerReset (initialValue = this.getInitialValue()) {
    this.mediator.reset(initialValue);
  }

  triggerSubmit (ev) {
    if (ev) {
      ev.preventDefault();
    }

    const flush = this.mediator.transaction('triggerSubmit');

    this.mediator.submitStart();

    if (!this.getMeta().valid) {
      this.mediator.submit();
      flush();
      return Promise.reject(null); // eslint-disable-line prefer-promise-reject-errors
    }

    if (!this.props.onSubmit) {
      this.mediator.submitDone();
      flush();
      return Promise.reject(null); // eslint-disable-line prefer-promise-reject-errors
    }

    const value = this.getValue();

    flush();

    return new Promise(() => {
      return Promise.resolve(this.props.onSubmit(value));
    }).then(
      () => {
        this.mediator.submitDone();
      },
      (err) => {
        this.mediator.submitFailed(err);

        return Promise.reject(err);
      }
    );
  }

  validate (value) {
    const { validate } = this.props;
    const flush = this.mediator.transaction('validate');

    const validateIsArray = Array.isArray(validate);

    if (!validate || (validateIsArray && !validate.length)) {
      this.mediator.changeValidity(null);
      flush();
      return true;
    }

    if (this._prevValidate === validate && value === this._prevValidatedValue) {
      flush();

      return !this.getMeta().error;
    }

    let error = null;
    const _validate = validateIsArray ? composeValidators(...validate) : validate;

    try {
      error = _validate(value);
      this._prevValidate = validate;
      this._prevValidatedValue = value;
    } catch (err) {
      this.mediator.sysValidateError(err);
      flush();
      return false;
    }

    this.mediator.sysValidateError(null);
    this.mediator.changeValidity(error);

    flush();

    return !error;
  }

  calcParams () {
    const state = this.getState();

    const meta = this.getMeta();

    const formFieldMeta = this.getClothesIsolatedMediatorMeta();

    let params = {
      triggerChange: this.triggerChange,
      triggerReset: this.triggerReset,

      valid: meta.valid,
      invalid: !meta.valid,
      hasException: meta.hasException,
      error: meta.hasException ? this.props.exceptionMessage : meta.error,
      invalidChildren: this.mediator.dehydrateNames(meta.errorsIn).filter(Boolean),

      value: this.getValue(),

      pristine: !meta.touched,
      touched: meta.touched,

      active: meta.active,
      activated: meta.activated,

      form: {
        valid: formFieldMeta.valid,
        invalid: !formFieldMeta.valid,
        hasException: formFieldMeta.hasException,

        active: formFieldMeta.active,
        activated: formFieldMeta.activated,

        touched: formFieldMeta.touched,
        pristine: !formFieldMeta.touched,

        submitted: formFieldMeta.submitted,
        submittedTimes: formFieldMeta.submittedTimes,
        submitFailed: formFieldMeta.submitFailed,
        submitSuccess: formFieldMeta.submitSuccess,
        submitting: formFieldMeta.submitting
      }
    };

    if (this.isForm) {
      params = {
        ...params,
        triggerSubmit: this.triggerSubmit,
        submitted: meta.submitted,
        submittedTimes: meta.submittedTimes,
        submitFailed: meta.submitFailed,
        submitSuccess: meta.submitSuccess,
        submitting: meta.submitting
      };
    }

    if (this.debug) {
      params.$field = state;
      params.$state = this.mediator._store._state;
    }

    return params;
  }

  _calcParams () {
    if (this._valueIsPostponed) {
      if (this._prevPostponedValue === this._postponedValue) {
        return this._prevParams;
      }

      this._prevPostponedValue = this._postponedValue;
    } else if (this._paramsVersion === this.mediator.version()) {
      return this._prevParams;
    }

    this._paramsVersion = this.mediator.version();

    const params = this.calcParams();

    this._prevParams = params;

    return params;
  }

  renderChildren () {
    const { children } = this.props;

    this._renderedVersion = this.mediator.version();

    return isFunction(children) ? children(this._calcParams()) : children;
  }

  renderInner () {
    return this.renderChildren();
  }

  render () {
    return this.renderInner();
  }

  subscribeGlobalStreams () {
    this.onDestroy(change.subscribe(this.componentID, (componentID, value) => this.triggerChange(value)));

    this.onDestroy(reset.subscribe(this.componentID, (initialValue) => this.triggerReset(initialValue)));

    this.onDestroy(initialize.subscribe(this.componentID, () => {
      // TODO: trigger initialize
    }));

    this.onDestroy(submit.subscribe(this.componentID, () => this.triggerSubmit()));
  }

  _refreshIfChanged () {
    const currentVersion = this.mediator.version();

    if (this.share !== SHARE.NONE || this._prevVersion === currentVersion) {
      return;
    }

    this._prevVersion = currentVersion;

    if (this.debug) {
      consoleLog(`TRIGGER SET STATE ${this.componentID}`);
      consoleTime(`RERENDERED ${this.componentID}`);
    }

    this._updateComponentState({
      form: this.mediator.getFullState()
    });
  }

  _unwatchMediatorChange () {}

  _watchMediatorChange () {
    this._unwatchMediatorChange();

    this._unwatchMediatorChange = this.mediator.onceChange(() => {
      this._refreshIfChanged();
    });
  }

  componentWillMount () {
  }

  componentDidMount () {
    //console.log('INIT', this.componentID);
    this.mediator.init();
    this.validate(this.getValue());
    this.flushInitializeTransaction();
    this.subscribeGlobalStreams();
    this._refreshIfChanged();
    this._watchMediatorChange();
  }

  componentWillUnmount () {
    this._destroy$.emit();
    this._destroy$.destroy();
    this.mediator.destroy();
  }

  componentDidUpdate (prevProps, prevState) {
    this._unwatchMediatorChange();

    if (this.debug && this.share === SHARE.NONE) {
      consoleTimeEnd(`RERENDERED ${this.componentID}`);
    }

    const flush = this.mediator.transaction('componentDidUpdate');

    const valueVersion = this.getState().valueVersion;

    if (this._valueIsPostponed && this._prevValueVersion !== valueVersion) {
      this._prevValueVersion = valueVersion;
      this._valueIsPostponed = false;
    }

    this.onUpdate(prevProps, prevState);

    const meta = this.getMeta();

    if (this.props.onChangeValidity && this._prevValidity !== meta.valid) {
      this.props.onChangeValidity(meta.valid);
    }

    this._prevValidity = meta.valid;

    flush();

    this._refreshIfChanged();

    this._watchMediatorChange();
  }

  onUpdate (prevProps) {
    const val = this.getValue();

    if (process.env.NODE_ENV !== 'production') {
      assertChangeProp(this.props, prevProps, 'id');
      assertChangeProp(this.props, prevProps, 'share');
      assertChangeProp(this.props, prevProps, 'form');
      assertChangeProp(this.props, prevProps, 'debug');
      assertChangeProp(this.props, prevProps, 'autoClean');
      assertAddRemoveProp(this.props, prevProps, 'value');
      assertAddRemoveProp(this.props, prevProps, 'name');
      assertAddRemoveProp(this.props, prevProps, 'debug');
      assertAddRemoveProp(this.props, prevProps, 'onChange');
      assertAddRemoveProp(this.props, prevProps, 'onChangeMeta');
      assertAddRemoveProp(this.props, prevProps, 'meta');
      assertAddRemoveProp(this.props, prevProps, 'form');
      assertAddRemoveProp(this.props, prevProps, 'share');
      assertAddRemoveProp(this.props, prevProps, 'id');
      assertAddRemoveProp(this.props, prevProps, 'autoClean');

      if (!isNil(val)) {
        assertType(`value of field "${this.componentID}" or null/undefined`, this.valueType, val);
      }
    }

    if (this.props.name !== prevProps.name) {
      this.mediator.rename(this.props.name);
    }

    this._notifyValueOnChange(val);

    this.validate(val);
  }
}
<|endoftext|>


<commit_before>/**
 * ActionManager 相关测试
 */
var ActionManagerLayer = cc.Layer.extend({
	ctor: function(){
		this._super();

		// 窗体的宽度
		var size = cc.winSize;

		/// 平移测试
		var spriteMoveTo = cc.Sprite(res.Sprite_jpg);
		spriteMoveTo.x = 40;
		spriteMoveTo.y = 40;
		spriteMoveTo.runAction(cc.moveTo(5, cc.p(600, 40)))
		this.addChild(spriteMoveTo);

		// 返回菜单
		var returnMenu = new cc.MenuItemFont(
				"返回",
				function () {
					cc.log("Menu is clicked!");
					cc.director.runScene(new MenuScene());
				}, this);
		returnMenu.attr({
			x: size.width - 60,
			y: size.height - 20,
			anchorX: 0.5,
			color: cc.color(255, 0, 0, 255),
			anchorY: 0.5
		});

		var menu = new cc.Menu(returnMenu);
		menu.x = 0;
		menu.y = 0;
		this.addChild(menu, 1);

	}
});

/**
 * 场景
 */
var ActionManagerScene = cc.Scene.extend({
	onEnter: function(){
		this._super();

		var layer = new ActionManagerLayer();
		this.addChild(layer);
	}
});<commit_msg>合并多个动画<commit_after>/**
 * ActionManager 相关测试
 */
var ActionManagerLayer = cc.Layer.extend({
	ctor: function(){
		this._super();

		// 窗体的宽度
		var size = cc.winSize;

		/// 1,moveTo测试
//		var spriteMoveTo = cc.Sprite(res.Sprite_jpg);
//		spriteMoveTo.x = 40;
//		spriteMoveTo.y = 40;
//		this.addChild(spriteMoveTo, 2);
//		// 向右移动
//		spriteMoveTo.runAction(cc.moveTo(5, cc.p(600, 400)));
//
//		/// 2,旋转
//		var spriteRotate = cc.Sprite(res.Sprite_jpg);
//		spriteRotate.x = size.width / 2;
//		spriteRotate.y = 120;
//		this.addChild(spriteRotate, 2);
//		spriteRotate.runAction(cc.rotateBy(1.5, 90));
//
//		/// 3,组合运动
//		var spriteSequence = cc.Sprite(res.Sprite_jpg);
//		spriteSequence.x = size.width - 40;
//		spriteSequence.y = 40;
//		this.addChild(spriteSequence, 2);
//		// 1.4秒之后消失
//		spriteSequence.runAction(cc.sequence(
//				cc.delayTime(1.4),
//				cc.fadeOut(1.1))
//		);
//
//		/// 4,moveBy测试
//		var spriteMoveBy = cc.Sprite(res.Sprite_jpg);
//		spriteMoveBy.x = 40;
//		spriteMoveBy.y = size.height / 2;
//		this.addChild(spriteMoveBy, 2);
//		// 向右移动
//		spriteMoveBy.runAction(cc.moveBy(2, cc.p(600, 400)));
//
		/**
		 * cc.MoveTo是“移动到这里"，而cc.MoveBy则是“相对于之前点再移动”，通俗一点就是说这里需要两个坐标pos1（x1，y1），pos2（x2，y2）。
			如果是cc.MoveTo的话，就是将对象由pos1移动到pos2，
			而cc.MoveBy则是说对象的终坐标是在pos1的基础上再加上（矢量相加）pos2，终坐标pos3=pos1+pos2。
		 */

		/// 5,暂停后再继续运动
		var spritePause = new cc.Sprite(res.Sprite_jpg);
		spritePause.x = 200;
		spritePause.y = 200;
		this.addChild(spritePause, 2, 1001);

		var action = cc.moveBy(5, cc.p(450, 0));
		spritePause.runAction(action);
		this.getActionManager().pauseTarget(spritePause);

//		cc.director.getActionManager().addAction(action, spritePause, true);

		this.schedule(function(){
			cc.log('in schedule---');
			var node = this.getChildByTag(1001);
			this.getActionManager().resumeTarget(node);
		}, 3);

		// 返回菜单
		var returnMenu = new cc.MenuItemFont(
				"返回",
				function () {
					cc.log("Menu is clicked!");
					cc.director.runScene(new MenuScene());
				}, this);
		returnMenu.attr({
			x: size.width - 60,
			y: size.height - 20,
			anchorX: 0.5,
			color: cc.color(255, 0, 0, 255),
			anchorY: 0.5
		});

		var menu = new cc.Menu(returnMenu);
		menu.x = 0;
		menu.y = 0;
		this.addChild(menu, 1);

	}
});

/**
 * 场景
 */
var ActionManagerScene = cc.Scene.extend({
	onEnter: function(){
		this._super();

		var layer = new ActionManagerLayer();
		this.addChild(layer);
	}
});<|endoftext|>