<commit_before>var fs = require("fs");
var path = require("path");

var FILE_PATHS = {
    ROOT: "/sys/class/gpio",
    EXPORT_FILE: "export",
    UNEXPORT_FILE: "unexport"
};
var GPIOReference = {

    gpios: [
        106, 107, 180, 181, 172, 173, 182, 124,
        25, 22, 14, 15, 16, 17, 18, 19,
        20, 21, 203, 202, 177, 176, 175, 174,
        119, 124, 127, 116, 7, 6, 5, 4
    ],

    DIRECTION: {
        INPUT: "in",
        OUTPUT: "out"
    },
    VALUE: {
        HIGH: "1",
        LOW: "0"
    },

    fromPin: function (pinNumber) {
        if (16 > pinNumber || pinNumber > 47) throw new Error("Invalid pin number.");
        return GPIOReference.gpios[pinNumber - 16];
    },
    isValid: function (gpioNumber) {
        return GPIOReference.gpios.indexOf(gpioNumber) > -1;
    }

};

function GPIO(num) {
    if (num) {
        this.fromGpio(num);
    }
}

GPIO.prototype = {

    currentGpioNumber: null,
    currentGPIO: function () {
        if (!this.currentGpioNumber) throw new Error("Missing GPIO number.");
        return this.currentGpioNumber;
    },

    fromPin: function (num) {
        this.currentGpioNumber = GPIOReference.fromPin(num);
        return this;
    },
    fromGpio: function (gpioNumber) {
        if (!GPIOReference.isValid(gpioNumber)) throw new Error("GPIO number (" + gpioNumber + ") is out of range.");
        this.currentGpioNumber = gpioNumber;
        return this;
    },

    export: function (callback) {
        var currentNum = this.currentGPIO();
        var exportFilePath = FILE_PATHS.ROOT + path.sep + FILE_PATHS.EXPORT_FILE;
        File.exists(FILE_PATHS.ROOT + path.sep + "gpio" + currentNum,
            function () {
                // already exported, do nothing
            },
            function () {
                File.write(currentNum.toString(), exportFilePath, function () {
                    if (callback) callback();
                }, "w");
            }
        );

    },
    unexport: function (callback) {
        var currentNum = this.currentGPIO();
        var exportFilePath = FILE_PATHS.ROOT + path.sep + FILE_PATHS.UNEXPORT_FILE;
        File.exists(FILE_PATHS.ROOT + path.sep + "gpio" + currentNum,
            function () {
                File.write(currentNum.toString(), exportFilePath, function () {
                    if (callback) callback();
                }, "w");
            },
            function () {
                // already unexported, do nothing
            }
        );
    },

    setDirection: function (direction, callback) {
        if ([GPIOReference.DIRECTION.INPUT, GPIOReference.DIRECTION.OUTPUT].indexOf(direction) < 0) throw new Error("Invalid direction.");
        var currentGPIO = this.currentGPIO();
        this.export(function () {
            File.createDir(FILE_PATHS.ROOT + path.sep + "gpio" + currentGPIO, function () {
                var directionPath = FILE_PATHS.ROOT + path.sep + "gpio" + currentGPIO + path.sep + "direction";
                File.write(direction, directionPath, function () {
                    if (callback) callback();
                });
            });
        });
    },
    getDirection: function (callback) {
        var currentNum = this.currentGPIO();
        this.export(function () {
            var directionPath = FILE_PATHS.ROOT + path.sep + "gpio" + currentNum + path.sep + "direction";
            File.read(directionPath, function (data) {
                callback(data);
            });
        });
    },
    setValue: function (value, callback) {
        if ([GPIOReference.VALUE.HIGH, GPIOReference.VALUE.LOW].indexOf(value) < 0) throw new Error("Invalid value.");
        var currentNum = this.currentGPIO();
        this.export(function () {
            File.createDir(FILE_PATHS.ROOT + path.sep + "gpio" + currentNum, function () {
                var valuePath = FILE_PATHS.ROOT + path.sep + "gpio" + currentNum + path.sep + "value";
                File.write(value, valuePath, function () {
                    if (callback) callback();
                });
            });
        });
    },
    getValue: function (callback) {
        var currentNum = this.currentGPIO();
        var valuePath = FILE_PATHS.ROOT + path.sep + "gpio" + currentNum + path.sep + "value";
        this.export(function () {
            File.read(valuePath, function (data) {
                callback(data);
            });
        });
    },
    watchValue: function (callback) {
        var currentNum = this.currentGPIO();
        var valuePath = FILE_PATHS.ROOT + path.sep + "gpio" + currentNum + path.sep + "value";
        var instance = this;
        File.exists(valuePath,
            function () {
                File.watch(valuePath, function () {
                    callback();
                });
            },
            function () {
                instance.export(function () {
                    File.watch(valuePath, function () {
                        callback();
                    });
                });
            }
        )

    }
};

var File = {
    createDir: function (path, callback) {
        fs.mkdir(path, function () {
            callback();
        })
    },
    exists: function (path, success, failure) {
        fs.access(path, function (err) {
            if (!err) success(); else failure();
        });
    },
    read: function (filename, callback) {
        fs.readFile(filename, "utf-8", function (err, data) {
            if (err) throw err;
            callback(data);
        });
    },
    watch: function (filename, callback) {
        fs.watch(filename, function (event, filename) {
            if ("change" === event) callback();
        });
    },
    write: function (data, filePath, callback, mode) {
        console.log("trying to write " + data + " in " + filePath);
        if (!mode) mode = "w";
        try {
            var fd = fs.openSync(filePath, mode, function (err) {
                if (err) {
                    console.log("Could not write " + data + " in " + filePath + " | " + mode);
                    throw err;
                }
            });
            fs.writeSync(fd, data, "utf-8");
        } catch (err) {
            if ("EBUSY" === err.errno) {
                this.write(data, filePath, callback, mode);
            }
        }
        callback();
    }
};

module.exports = {
    gpios: GPIOReference.gpios,
    DIRECTION: GPIOReference.DIRECTION,
    VALUE: GPIOReference.VALUE,
    GPIO: GPIO
};
<commit_msg>Adds callback back<commit_after>var fs = require("fs");
var path = require("path");

var FILE_PATHS = {
    ROOT: "/sys/class/gpio",
    EXPORT_FILE: "export",
    UNEXPORT_FILE: "unexport"
};
var GPIOReference = {

    gpios: [
        106, 107, 180, 181, 172, 173, 182, 124,
        25, 22, 14, 15, 16, 17, 18, 19,
        20, 21, 203, 202, 177, 176, 175, 174,
        119, 124, 127, 116, 7, 6, 5, 4
    ],

    DIRECTION: {
        INPUT: "in",
        OUTPUT: "out"
    },
    VALUE: {
        HIGH: "1",
        LOW: "0"
    },

    fromPin: function (pinNumber) {
        if (16 > pinNumber || pinNumber > 47) throw new Error("Invalid pin number.");
        return GPIOReference.gpios[pinNumber - 16];
    },
    isValid: function (gpioNumber) {
        return GPIOReference.gpios.indexOf(gpioNumber) > -1;
    }

};

function GPIO(num) {
    if (num) {
        this.fromGpio(num);
    }
}

GPIO.prototype = {

    currentGpioNumber: null,
    currentGPIO: function () {
        if (!this.currentGpioNumber) throw new Error("Missing GPIO number.");
        return this.currentGpioNumber;
    },

    fromPin: function (num) {
        this.currentGpioNumber = GPIOReference.fromPin(num);
        return this;
    },
    fromGpio: function (gpioNumber) {
        if (!GPIOReference.isValid(gpioNumber)) throw new Error("GPIO number (" + gpioNumber + ") is out of range.");
        this.currentGpioNumber = gpioNumber;
        return this;
    },

    export: function (callback) {
        var currentNum = this.currentGPIO();
        var exportFilePath = FILE_PATHS.ROOT + path.sep + FILE_PATHS.EXPORT_FILE;
        File.exists(FILE_PATHS.ROOT + path.sep + "gpio" + currentNum,
            function () {
                // already exported
                if (callback) callback();
            },
            function () {
                File.write(currentNum.toString(), exportFilePath, function () {
                    if (callback) callback();
                }, "w");
            }
        );

    },
    unexport: function (callback) {
        var currentNum = this.currentGPIO();
        var exportFilePath = FILE_PATHS.ROOT + path.sep + FILE_PATHS.UNEXPORT_FILE;
        File.exists(FILE_PATHS.ROOT + path.sep + "gpio" + currentNum,
            function () {
                File.write(currentNum.toString(), exportFilePath, function () {
                    if (callback) callback();
                }, "w");
            },
            function () {
                // already unexported
                if (callback) callback();
            }
        );
    },

    setDirection: function (direction, callback) {
        if ([GPIOReference.DIRECTION.INPUT, GPIOReference.DIRECTION.OUTPUT].indexOf(direction) < 0) throw new Error("Invalid direction.");
        var currentGPIO = this.currentGPIO();
        this.export(function () {
            File.createDir(FILE_PATHS.ROOT + path.sep + "gpio" + currentGPIO, function () {
                var directionPath = FILE_PATHS.ROOT + path.sep + "gpio" + currentGPIO + path.sep + "direction";
                File.write(direction, directionPath, function () {
                    if (callback) callback();
                });
            });
        });
    },
    getDirection: function (callback) {
        var currentNum = this.currentGPIO();
        this.export(function () {
            var directionPath = FILE_PATHS.ROOT + path.sep + "gpio" + currentNum + path.sep + "direction";
            File.read(directionPath, function (data) {
                callback(data);
            });
        });
    },
    setValue: function (value, callback) {
        if ([GPIOReference.VALUE.HIGH, GPIOReference.VALUE.LOW].indexOf(value) < 0) throw new Error("Invalid value.");
        var currentNum = this.currentGPIO();
        this.export(function () {
            File.createDir(FILE_PATHS.ROOT + path.sep + "gpio" + currentNum, function () {
                var valuePath = FILE_PATHS.ROOT + path.sep + "gpio" + currentNum + path.sep + "value";
                File.write(value, valuePath, function () {
                    if (callback) callback();
                });
            });
        });
    },
    getValue: function (callback) {
        var currentNum = this.currentGPIO();
        var valuePath = FILE_PATHS.ROOT + path.sep + "gpio" + currentNum + path.sep + "value";
        this.export(function () {
            File.read(valuePath, function (data) {
                callback(data);
            });
        });
    },
    watchValue: function (callback) {
        var currentNum = this.currentGPIO();
        var valuePath = FILE_PATHS.ROOT + path.sep + "gpio" + currentNum + path.sep + "value";
        var instance = this;
        File.exists(valuePath,
            function () {
                File.watch(valuePath, function () {
                    callback();
                });
            },
            function () {
                instance.export(function () {
                    File.watch(valuePath, function () {
                        callback();
                    });
                });
            }
        )

    }
};

var File = {
    createDir: function (path, callback) {
        fs.mkdir(path, function () {
            callback();
        })
    },
    exists: function (path, success, failure) {
        fs.access(path, function (err) {
            if (!err) success(); else failure();
        });
    },
    read: function (filename, callback) {
        fs.readFile(filename, "utf-8", function (err, data) {
            if (err) throw err;
            callback(data);
        });
    },
    watch: function (filename, callback) {
        fs.watch(filename, function (event, filename) {
            if ("change" === event) callback();
        });
    },
    write: function (data, filePath, callback, mode) {
        console.log("trying to write " + data + " in " + filePath);
        if (!mode) mode = "w";
        try {
            var fd = fs.openSync(filePath, mode, function (err) {
                if (err) {
                    console.log("Could not write " + data + " in " + filePath + " | " + mode);
                    throw err;
                }
            });
            fs.writeSync(fd, data, "utf-8");
        } catch (err) {
            if ("EBUSY" === err.errno) {
                this.write(data, filePath, callback, mode);
            }
        }
        callback();
    }
};

module.exports = {
    gpios: GPIOReference.gpios,
    DIRECTION: GPIOReference.DIRECTION,
    VALUE: GPIOReference.VALUE,
    GPIO: GPIO
};
<|endoftext|>


<commit_before>// http://ilikekillnerds.com/2016/04/webpack-typescript-aurelia-wallaby-js/
// https://github.com/wallabyjs/wallaby-jspm-sample/blob/master/wallaby.js
"use strict";

module.exports = function (wallaby) {
    var rootDir = process.cwd();
    return {
        files: [
            { pattern: "jspm_packages/system.js", instrument: false },
            { pattern: "node_modules/chai/chai.js", instrument: false },
            { pattern: "node_modules/chai-as-promised/chai-as-promised.js", instrument: false },
            { pattern: "node_modules/sinon/lib/sinon.js", instrument: false },
            { pattern: "jspm.config.js", instrument: false },
            { pattern: "Modules/JavaScript.Pipeline/json.js", instrument: false, load: false },
            { pattern: "package.json", instrument: false, load: false },

            { pattern: "Specifications/**/given/*.js", load: false },
            { pattern: "Source/**/*.js", load: false }
        ],
        tests: [
            { pattern: "!Specifications/**/given/*.js", load: false },
            { pattern: "Specifications/**/*.js", load: false }
        ],

        compilers: {
            "**/*.js": wallaby.compilers.babel({
                babel: require("babel-core")
            })
        },

        env: {
            kind: "electron"
        },

        middleware: (app, express) => {
            app.use("/jspm_packages", express.static(require("path").join(rootDir, "jspm_packages")));
        },

        setup: function (wallaby) {
            wallaby.delayStart();

            window.expect = chai.expect;
            var should = chai.should();

            // Note:
            // The reason for the hardcoding of this path is that the "setup" function  is ran in a different
            // context. Ideally we would read something like the .gitmodules file and dynamically figure this out
            // or even use __dirname or something to dynamically get the folder in here
            System.import("./package.json!./Modules/JavaScript.Pipeline/json.js").then(function (pkg) {
                var systemConfig = {
                    defaultJSExtensions: true,
                    transpiler: false,
                    packages: {}
                };

                systemConfig.packages[pkg.name] = {
                    format: "cjs",
                    meta: {"*.js": {}}
                };

                System.config(systemConfig);

                var promises = [];
                for (var i = 0, len = wallaby.tests.length; i < len; i++) {
                    promises.push(System['import'](wallaby.tests[i]));
                }

                Promise.all(promises).then(function () {
                    wallaby.start();
                }).catch(function (e) {
                    setTimeout(function () {
                        throw e;
                    }, 0);
                });

            });

        },

        testFramework: "jasmine",

        debug: true
    };
};<commit_msg>Fixing the path to sinon -> pkg folder<commit_after>// http://ilikekillnerds.com/2016/04/webpack-typescript-aurelia-wallaby-js/
// https://github.com/wallabyjs/wallaby-jspm-sample/blob/master/wallaby.js
"use strict";

module.exports = function (wallaby) {
    var rootDir = process.cwd();
    return {
        files: [
            { pattern: "jspm_packages/system.js", instrument: false },
            { pattern: "node_modules/chai/chai.js", instrument: false },
            { pattern: "node_modules/chai-as-promised/chai-as-promised.js", instrument: false },
            { pattern: "node_modules/sinon/pkg/sinon.js", instrument: false },
            { pattern: "jspm.config.js", instrument: false },
            { pattern: "Modules/JavaScript.Pipeline/json.js", instrument: false, load: false },
            { pattern: "package.json", instrument: false, load: false },

            { pattern: "Specifications/**/given/*.js", load: false },
            { pattern: "Source/**/*.js", load: false }
        ],
        tests: [
            { pattern: "!Specifications/**/given/*.js", load: false },
            { pattern: "Specifications/**/*.js", load: false }
        ],

        compilers: {
            "**/*.js": wallaby.compilers.babel({
                babel: require("babel-core")
            })
        },

        env: {
            kind: "electron"
        },

        middleware: (app, express) => {
            app.use("/jspm_packages", express.static(require("path").join(rootDir, "jspm_packages")));
        },

        setup: function (wallaby) {
            wallaby.delayStart();

            window.expect = chai.expect;
            var should = chai.should();

            // Note:
            // The reason for the hardcoding of this path is that the "setup" function  is ran in a different
            // context. Ideally we would read something like the .gitmodules file and dynamically figure this out
            // or even use __dirname or something to dynamically get the folder in here
            System.import("./package.json!./Modules/JavaScript.Pipeline/json.js").then(function (pkg) {
                var systemConfig = {
                    defaultJSExtensions: true,
                    transpiler: false,
                    packages: {}
                };

                systemConfig.packages[pkg.name] = {
                    format: "cjs",
                    meta: {"*.js": {}}
                };

                System.config(systemConfig);

                var promises = [];
                for (var i = 0, len = wallaby.tests.length; i < len; i++) {
                    promises.push(System['import'](wallaby.tests[i]));
                }

                Promise.all(promises).then(function () {
                    wallaby.start();
                }).catch(function (e) {
                    setTimeout(function () {
                        throw e;
                    }, 0);
                });

            });

        },

        testFramework: "jasmine",

        debug: true
    };
};<|endoftext|>


<commit_before>'use strict';

const request = require('request');
const redis = require('redis');
const twilio = require('twilio');
const util = require('util');

// Application settings.
// Twilio account secure ID.
var twilioAccountSid;
// Twilio API secure ID.
var twilioApiSid;
// Twilio API secret.
var twilioApiSecret;
// Sender phone number of the sms notification.
var smsSenderPhoneNumber;
// Your twitch secret aka client-id.
var twitchClientId;
// Object what contains the phone numbers to send sms.
var smsReceiversPhoneNumberArray;
// Channels what we are following.
var followingChannelNamesArray;
// Redis db uri
var redisUri;
// Number of the sms have to send.
var smsCounter;
// Send sms when the cannel goes offline.
var sendWhenOffline;

// SMS messages.
const goLiveMessage = '%s is streaming %s from %s.';
const goOfflineMessage = '%s goes offline!';

// Redis client.
var redisClient;

// Create URL for the specified channel.
function createUrl(channelName) {
  return 'https://api.twitch.tv/kraken/streams/' + channelName
}

// Initialize the enviornment variables.
function init() {
  twilioAccountSid = process.env.TWILIO_ACCOUNT_SID;
  twilioApiSid = process.env.TWILIO_API_SID;
  twilioApiSecret = process.env.TWILIO_API_SECRET;
  smsSenderPhoneNumber = process.env.SMS_SENDER_PHONE_NUMBER;
  // Get the phone numbers as an array
  smsReceiversPhoneNumberArray = process.env.NOTIFICATION_PHONE_NUMBERS ? process.env.NOTIFICATION_PHONE_NUMBERS.split(',') : [];
  twitchClientId = process.env.TWITCH_CLIENT_ID;
  // Get the channels name as an array
  followingChannelNamesArray = process.env.CHANNEL_NAMES ? process.env.CHANNEL_NAMES.split(',') : [];
  redisUri = process.env.REDIS_URL;
  sendWhenOffline = process.env.SEND_WHEN_OFFLINE ? true : false;
  // Set the 'have to send' sms number from the smsReceiversPhoneNumberArray multiply by the channels number.
  smsCounter = smsReceiversPhoneNumberArray.length * followingChannelNamesArray.length;
}

// Send sms via Twilio
function sendSms(fromNumber, toNumber, message, callback) {
  // Authenticate a new twilio REST client.
  var sender = new twilio.RestClient(twilioApiSid, twilioApiSecret).accounts(twilioAccountSid);
  // Create then send an sms.
  sender.messages.create({
      to: toNumber,
      from: fromNumber,
      body: message
  }, function(err, message) {
  	if (err) {
      setImmediate(callback, new Error('Cannot send the sms to the number: ' + fromNumber + ' Error: ' + err.code));
    } else {
      setImmediate(callback, null, 'SMS sent! SID: ' + message.sid);
    }
  });
}

// Create message content.
function createMessageContent(isOnline, channelName, response) {
  // Message content.
  var message;
  if (isOnline) {
    message = util.format(goLiveMessage, response.stream.channel.display_name,
                              response.stream.game, response.stream.created_at.split('T')[1].replace('Z',''));
  } else {
    message = util.format(goOfflineMessage, channelName);
  }

  return message;
}

// Send sms to all of the telephone numbers specified in the config
function sendSmsToUsers(isOnline, channelName, response, callback) {
  // If we don't want to send SMS if the user became offline.
  if (!sendWhenOffline && !isOnline) {
    setImmediate(callback, null, 'Offline but don\'t send because of the settings.');
    return;
  }
  // Create the sms message content.
  var messageContent = createMessageContent(isOnline, channelName, response);
  // Iterated through the phone numbers.
  for (var i = 0; i < smsReceiversPhoneNumberArray.length; ++i) {
    // Send sms to each number in the array.
    sendSms(smsSenderPhoneNumber, smsReceiversPhoneNumberArray[i], messageContent, function(err, res) {
      if (err) {
        setImmediate(callback, new Error(err));
      }
      if (res) {
        setImmediate(callback, null, res);
      }
    });
  }
}

// Check the channel. If online return true otherwise false,
function checkChannel(channelName, callback) {
  // The request object.
  var requestOptions = {
    url: createUrl(channelName),
    headers: {
      'Client-ID': twitchClientId
    }
  };
  // Get response from the twitch api.
  request(requestOptions, function(error, response, body) {
    // Error occurred.
    if (error) {
      setImmediate(callback, error);
    }
    // If no response from the server.
    else if (!response) {
      setImmediate(callback, new Error('No response from the server.'));
    }
    // If empty body in the response.
    else if (!body) {
      setImmediate(callback, new Error('Empty body in the response.'));
    } else {
      var bodyObj = null;
      try {
        // Parse the body string to JSON.
        bodyObj = JSON.parse(body);
      } catch (ex) {
        setImmediate(callback, new Error('Invalid body JSON.'));
        return;
      }

      // If online true otherwise false;
      var isOnline = bodyObj.stream ? true : false;
      // Let the function decide shoud we send sms to users.
      shouldSendSms(channelName, isOnline, bodyObj, function(err, res) {
        if (err) {
          setImmediate(callback, err);
        }
        if (res) {
          setImmediate(callback, null, res);
        }
      });
    }
  });
}

// Connect to redis
function connectToRedis(callback) {
  try {
    // Create the client.
    redisClient = redis.createClient(redisUri);
  } catch (ex){
    setImmediate(callback, new Error('Cannot create redis client. Exception: ' + ex));
    return;
  }
  // Connect to the db.
  redisClient.on('ready', function() {
    setImmediate(callback, null);
  });

  redisClient.on('error', function(err) {
    setImmediate(callback, err);
  });

}

// Check old and new status of the cannel in the db
function shouldSendSms(channelName, isOnline, bodyObj, callback) {
  redisClient.getset(channelName, isOnline, function(err, resp) {
    // If get error, finish the function.
    if (err) {
      setImmediate(callback, new Error('Error occured while trying to getset to the redis db.'))
      return;
    }

    // If no error let's send sms if the status changed.
    if (isOnline.toString() !== resp) {
      // Send sms based on change (from true to false or from false to true).
      sendSmsToUsers((isOnline === true && resp === 'false'), channelName, bodyObj, function(err, res) {
        if (err) {
          setImmediate(callback, err);
        } else {
          setImmediate(callback, null, res)
        }
      });
    } else {
      // No change.
      // Because we won't iterate through all of the number we have to set the smsCounter to zero manually.
      setSmsCountZero();
      setImmediate(callback, null, 'Status didn\'t change for: ' + channelName + ' Still ' + resp + '.');
    }
  });
}

// Check the sent sms number. Can we close the db and quit now?
function checkSmsCount() {
  // Minus one from the current because we get a response (error or res nevermind).
  smsCounter -= 1;
  // If there is no sms left,
  if (smsCounter <= 0) {
    // close the db connection.
    redisClient.quit();
  }
}

// Set the 'have to send' sms count to zero.
function setSmsCountZero() {
  smsCounter = 0;
}

// Start the script.
function start() {
  // Init the environment variables.
  init();

  // Make some basic validations.
  if (followingChannelNamesArray.length === 0) {
    console.error('ERROR: Please specify the channels to watch.');
  }
  else if (smsReceiversPhoneNumberArray.length === 0) {
    console.error('ERROR: Please specify the phone numbers which will receive the SMS alerts.');
  }
  else if (!redisUri) {
    console.error('ERROR: Please specify the Redis server to connect.');
  }
  else if (!twilioAccountSid || !twilioApiSid || !twilioApiSecret) {
    console.error('ERROR: Please specify all the Twilio credentials.');
  }
  else if (!smsSenderPhoneNumber) {
    console.error('ERROR: Please specify the SMS sender phone number.');
  }
  else if (!twitchClientId) {
    console.error('ERROR: Please specify the Twitch Client-ID.');
  } else {
    // Everything looks good.

    // First connect to redis.
    console.log('Connecting to the db...');
    // Create connection to the db.
    connectToRedis(function(err) {
      if (err) {
        console.error('ERROR: ' + err.toString());
      } else {
        console.log('Connected to redis db.');
        // Iterates through the given channel names.
        for (var i = 0; i < followingChannelNamesArray.length; ++i) {
          if (!followingChannelNamesArray[i]) {
            console.error('ERROR: Invalid channel name at: ' + i);
          } else {
            checkChannel(followingChannelNamesArray[i], function(err, res) {
              if (err) {
                console.error(err.toString());
                checkSmsCount();
              } else {
                console.log(res.toString());
                checkSmsCount();
              }
            });
          }
        }
      }
    });
  }
}

// Start the script.
start()
<commit_msg>Fix time for HUN.<commit_after>'use strict';

const request = require('request');
const redis = require('redis');
const twilio = require('twilio');
const util = require('util');

// Application settings.
// Twilio account secure ID.
var twilioAccountSid;
// Twilio API secure ID.
var twilioApiSid;
// Twilio API secret.
var twilioApiSecret;
// Sender phone number of the sms notification.
var smsSenderPhoneNumber;
// Your twitch secret aka client-id.
var twitchClientId;
// Object what contains the phone numbers to send sms.
var smsReceiversPhoneNumberArray;
// Channels what we are following.
var followingChannelNamesArray;
// Redis db uri
var redisUri;
// Number of the sms have to send.
var smsCounter;
// Send sms when the cannel goes offline.
var sendWhenOffline;

// SMS messages.
const goLiveMessage = '%s is streaming %s from %s.';
const goOfflineMessage = '%s goes offline!';

// Redis client.
var redisClient;

// Create URL for the specified channel.
function createUrl(channelName) {
  return 'https://api.twitch.tv/kraken/streams/' + channelName
}

// Initialize the enviornment variables.
function init() {
  twilioAccountSid = process.env.TWILIO_ACCOUNT_SID;
  twilioApiSid = process.env.TWILIO_API_SID;
  twilioApiSecret = process.env.TWILIO_API_SECRET;
  smsSenderPhoneNumber = process.env.SMS_SENDER_PHONE_NUMBER;
  // Get the phone numbers as an array
  smsReceiversPhoneNumberArray = process.env.NOTIFICATION_PHONE_NUMBERS ? process.env.NOTIFICATION_PHONE_NUMBERS.split(',') : [];
  twitchClientId = process.env.TWITCH_CLIENT_ID;
  // Get the channels name as an array
  followingChannelNamesArray = process.env.CHANNEL_NAMES ? process.env.CHANNEL_NAMES.split(',') : [];
  redisUri = process.env.REDIS_URL;
  sendWhenOffline = process.env.SEND_WHEN_OFFLINE ? true : false;
  // Set the 'have to send' sms number from the smsReceiversPhoneNumberArray multiply by the channels number.
  smsCounter = smsReceiversPhoneNumberArray.length * followingChannelNamesArray.length;
}

// Send sms via Twilio
function sendSms(fromNumber, toNumber, message, callback) {
  // Authenticate a new twilio REST client.
  var sender = new twilio.RestClient(twilioApiSid, twilioApiSecret).accounts(twilioAccountSid);
  // Create then send an sms.
  sender.messages.create({
      to: toNumber,
      from: fromNumber,
      body: message
  }, function(err, message) {
  	if (err) {
      setImmediate(callback, new Error('Cannot send the sms to the number: ' + fromNumber + ' Error: ' + err.code));
    } else {
      setImmediate(callback, null, 'SMS sent! SID: ' + message.sid);
    }
  });
}

// Create message content.
function createMessageContent(isOnline, channelName, response) {
  // Message content.
  var message;
  if (isOnline) {
    message = util.format(goLiveMessage, response.stream.channel.display_name,
                              response.stream.game, getTimeOnly(response.stream.created_at));
  } else {
    message = util.format(goOfflineMessage, channelName);
  }

  return message;
}

// Send sms to all of the telephone numbers specified in the config
function sendSmsToUsers(isOnline, channelName, response, callback) {
  // If we don't want to send SMS if the user became offline.
  if (!sendWhenOffline && !isOnline) {
    setImmediate(callback, null, 'Offline but don\'t send because of the settings.');
    return;
  }
  // Create the sms message content.
  var messageContent = createMessageContent(isOnline, channelName, response);
  // Iterated through the phone numbers.
  for (var i = 0; i < smsReceiversPhoneNumberArray.length; ++i) {
    // Send sms to each number in the array.
    sendSms(smsSenderPhoneNumber, smsReceiversPhoneNumberArray[i], messageContent, function(err, res) {
      if (err) {
        setImmediate(callback, new Error(err));
      }
      if (res) {
        setImmediate(callback, null, res);
      }
    });
  }
}

// Check the channel. If online return true otherwise false,
function checkChannel(channelName, callback) {
  // The request object.
  var requestOptions = {
    url: createUrl(channelName),
    headers: {
      'Client-ID': twitchClientId
    }
  };
  // Get response from the twitch api.
  request(requestOptions, function(error, response, body) {
    // Error occurred.
    if (error) {
      setImmediate(callback, error);
    }
    // If no response from the server.
    else if (!response) {
      setImmediate(callback, new Error('No response from the server.'));
    }
    // If empty body in the response.
    else if (!body) {
      setImmediate(callback, new Error('Empty body in the response.'));
    } else {
      var bodyObj = null;
      try {
        // Parse the body string to JSON.
        bodyObj = JSON.parse(body);
      } catch (ex) {
        setImmediate(callback, new Error('Invalid body JSON.'));
        return;
      }

      // If online true otherwise false;
      var isOnline = bodyObj.stream ? true : false;
      // Let the function decide shoud we send sms to users.
      shouldSendSms(channelName, isOnline, bodyObj, function(err, res) {
        if (err) {
          setImmediate(callback, err);
        }
        if (res) {
          setImmediate(callback, null, res);
        }
      });
    }
  });
}

// Connect to redis
function connectToRedis(callback) {
  try {
    // Create the client.
    redisClient = redis.createClient(redisUri);
  } catch (ex){
    setImmediate(callback, new Error('Cannot create redis client. Exception: ' + ex));
    return;
  }
  // Connect to the db.
  redisClient.on('ready', function() {
    setImmediate(callback, null);
  });

  redisClient.on('error', function(err) {
    setImmediate(callback, err);
  });

}

// Check old and new status of the cannel in the db
function shouldSendSms(channelName, isOnline, bodyObj, callback) {
  redisClient.getset(channelName, isOnline, function(err, resp) {
    // If get error, finish the function.
    if (err) {
      setImmediate(callback, new Error('Error occured while trying to getset to the redis db.'))
      return;
    }

    // If no error let's send sms if the status changed.
    if (isOnline.toString() !== resp) {
      // Send sms based on change (from true to false or from false to true).
      sendSmsToUsers((isOnline === true && resp === 'false'), channelName, bodyObj, function(err, res) {
        if (err) {
          setImmediate(callback, err);
        } else {
          setImmediate(callback, null, res)
        }
      });
    } else {
      // No change.
      // Because we won't iterate through all of the number we have to set the smsCounter to zero manually.
      setSmsCountZero();
      setImmediate(callback, null, 'Status didn\'t change for: ' + channelName + ' Still ' + resp + '.');
    }
  });
}

// Check the sent sms number. Can we close the db and quit now?
function checkSmsCount() {
  // Minus one from the current because we get a response (error or res nevermind).
  smsCounter -= 1;
  // If there is no sms left,
  if (smsCounter <= 0) {
    // close the db connection.
    redisClient.quit();
  }
}

// Set the 'have to send' sms count to zero.
function setSmsCountZero() {
  smsCounter = 0;
}

// Add two hour to the current time, because of the timezones.
function getTimeOnly(date) {
  var fullDate = new Date(date);
  // Add plus two hour.
  var newDate = new Date(fullDate.getTime() + 2*60*60*1000);

  // Create the string with the hourd, minutes and seconds only.
  var timeString = newDate.getHours() + ':' + newDate.getMinutes() + ':' + newDate.getSeconds();

  return timeString;
}

// Start the script.
function start() {
  // Init the environment variables.
  init();

  // Make some basic validations.
  if (followingChannelNamesArray.length === 0) {
    console.error('ERROR: Please specify the channels to watch.');
  }
  else if (smsReceiversPhoneNumberArray.length === 0) {
    console.error('ERROR: Please specify the phone numbers which will receive the SMS alerts.');
  }
  else if (!redisUri) {
    console.error('ERROR: Please specify the Redis server to connect.');
  }
  else if (!twilioAccountSid || !twilioApiSid || !twilioApiSecret) {
    console.error('ERROR: Please specify all the Twilio credentials.');
  }
  else if (!smsSenderPhoneNumber) {
    console.error('ERROR: Please specify the SMS sender phone number.');
  }
  else if (!twitchClientId) {
    console.error('ERROR: Please specify the Twitch Client-ID.');
  } else {
    // Everything looks good.

    // First connect to redis.
    console.log('Connecting to the db...');
    // Create connection to the db.
    connectToRedis(function(err) {
      if (err) {
        console.error('ERROR: ' + err.toString());
      } else {
        console.log('Connected to redis db.');
        // Iterates through the given channel names.
        for (var i = 0; i < followingChannelNamesArray.length; ++i) {
          if (!followingChannelNamesArray[i]) {
            console.error('ERROR: Invalid channel name at: ' + i);
          } else {
            checkChannel(followingChannelNamesArray[i], function(err, res) {
              if (err) {
                console.error(err.toString());
                checkSmsCount();
              } else {
                console.log(res.toString());
                checkSmsCount();
              }
            });
          }
        }
      }
    });
  }
}

// Start the script.
start()
<|endoftext|>


<commit_before>// See Purple/license.txt for Google BSD license
// Copyright 2011 Google, Inc. johnjbarton@johnjbarton.com

define(['../browser/remoteByWebInspector', '../resources/Resources', '../resources/Resource'], function (remoteByWebInspector, Resources, Resource) {
  var thePurple = window.purple;
  var Assembly = thePurple.Assembly;

  var networkEventHandler = new thePurple.PurplePart('networkEventHandler');

  networkEventHandler.getOrCreateResource = function(url) {
    var resource = Resources.get(url);
    if (!resource) {
      resource = Resource.new(url);
      Resources.append(url, resource);
    }
    return resource;
  };

  networkEventHandler.requests = {};

  // close over the handler here to narrow the interface to Resource
  // |this| will be bound to a Resource

  function fetchContent(fncOfContent, fncOfError) {
    if (this.requestId) {
      networkEventHandler.remote.setResponseCallbacks(fncOfContent, fncOfError);
      networkEventHandler.remote.Network.getResponseBody(this.requestId);
    } else {
      fncOfError("Resource not loaded: "+this.url);
    }
  }

  networkEventHandler.setRequestById = function(requestId, resource) {
    if (this.requests.hasOwnProperty(requestId)) {
      throw new Error("duplicate requestId, "+requestId+" something is wrong ");
    }
    this.requests[requestId] = resource;
    resource.fetchContent = fetchContent;
    resource.hasSource = true;
  };

  networkEventHandler.getRequestById = function(requestId) {
    if (this.requests.hasOwnProperty(requestId)) {
      return this.requests[requestId];
    } else {
      throw new Error("Expected resource at "+requestId+"but none was found");
    }
  };

  //---------------------------------------------------------------------------------------------
  // Implement Remote.events
  networkEventHandler.responseHandlers = {
    Network: {
        dataReceived: function(requestId, timestamp, dataLength, encodedDataLength){
          var resource = networkEventHandler.getRequestById(requestId);
          resource.progress = resource.progress || [];
          resource.progress.push({timestamp: timestamp, dataLength: dataLength, encodedDataLength: encodedDataLength});
        },
        loadingFailed: function(requestId, timestamp, errorText, canceled){
          var resource = networkEventHandler.getRequestById(requestId);
          resource.timestamps.loadingFailed = timestamp;
          resource.errorText = errorText;
          resource.canceled = canceled;
        },
        loadingFinished: function(requestId, timestamp){
          var resource = networkEventHandler.getRequestById(requestId);
          resource.timestamps.loadingFailed = timestamp;
        },
        requestServedFromCache: function(requestId){
          var resource = networkEventHandler.getRequestById(requestId);
          resource.servedFromCache = true;
        },
        requestServedFromMemoryCache: function(requestId, loaderId, documentURL, timestamp, initiator, cachedResource){
          var url = cachedResource.url;
          var resource = networkEventHandler.getOrCreateResource(url);
          resource.documentURL = documentURL;
          resource.requestId = requestId;
          resource.loaderId = loaderId;
          resource.timestamps = {"fromMemoryCache": timestamp};
          resource.initiator = initiator;
          resource.resource = cachedResource;
          networkEventHandler.setRequestById(requestId, resource);
          networkEventHandler.logger(resource);
        },
        requestWillBeSent: function(requestId, loaderId, documentURL, request, timestamp, initiator, stackTrace, redirectResponse){
          var url = request.url;
          var resource = networkEventHandler.getOrCreateResource(url);
          resource.documentURL = documentURL;
          resource.requestId = requestId;
          resource.loaderId = loaderId;
          resource.request = request;
          resource.timestamps = {"sent": timestamp};
          resource.initiator = initiator;
          resource.stackTrace = stackTrace;
          resource.redirectResponse = redirectResponse;
          networkEventHandler.setRequestById(requestId, resource);
          networkEventHandler.logger(resource);
        },
        responseRecieved: function(requestId, timestamp, type, response){
          var resource = networkEventHandler.getRequestById(requestId);
          resource.timestamps.responseRecieved = timestamp;
          resource.type = type;
          resource.response = response;
        }
      },
      WebNavigation: {
        onBeforeNavigate: function(details){
          networkEventHandler.logger(details);
        },
        onBeforeRetarget: function(details){
          networkEventHandler.logger(details);
        },
        onCommitted: function(details){
          networkEventHandler.logger(details);
        },
        onCompleted: function(details){
          networkEventHandler.logger(details);
        },
        onDOMContentLoaded: function(details){
          networkEventHandler.logger(details);
        },
        onErrorOccurred: function(details){
          networkEventHandler.logger(details);
        }
      }
  };

   //---------------------------------------------------------------------------------------------
  // Implement PurplePart

  networkEventHandler.connect = function(channel) {
      this.remote = remoteByWebInspector.create('networkRemote', this.responseHandlers);
      this.remote.connect(channel);
      this.logger = channel.recv.bind(channel);
	  this.remote.Network.enable();
  };

  networkEventHandler.disconnect = function(channel) {
      this.remote.Network.disable();
      Resources.disconnect(this.logger);
  };

  thePurple.registerPart(networkEventHandler);

  return networkEventHandler;
});<commit_msg>Use Q to handle the promise from the remote<commit_after>// See Purple/license.txt for Google BSD license
// Copyright 2011 Google, Inc. johnjbarton@johnjbarton.com

define(['../browser/remoteByWebInspector', '../resources/Resources', '../resources/Resource','../lib/q/q'],
function (          remoteByWebInspector,                Resources,                Resource,            Q) {
  var thePurple = window.purple;

  var networkEventHandler = new thePurple.PurplePart('networkEventHandler');

  networkEventHandler.getOrCreateResource = function(url) {
    var resource = Resources.get(url);
    if (!resource) {
      resource = Resource.new(url);
      Resources.append(url, resource);
    }
    return resource;
  };

  networkEventHandler.requests = {};

  // close over the handler here to narrow the interface to Resource
  // |this| will be bound to a Resource

  function fetchContent(fncOfContent, fncOfError) {
    if (this.requestId) {
      var responseBody = networkEventHandler.remote.Network.getResponseBody(this.requestId);
      Q.when(responseBody, fncOfContent, fncOfError);
    } else {
      fncOfError("Resource not loaded: "+this.url);
    }
  }

  networkEventHandler.setRequestById = function(requestId, resource) {
    if (this.requests.hasOwnProperty(requestId)) {
      throw new Error("duplicate requestId, "+requestId+" something is wrong ");
    }
    this.requests[requestId] = resource;
    resource.fetchContent = fetchContent;
    resource.hasSource = true;
  };

  networkEventHandler.getRequestById = function(requestId) {
    if (this.requests.hasOwnProperty(requestId)) {
      return this.requests[requestId];
    } else {
      throw new Error("Expected resource at "+requestId+"but none was found");
    }
  };

  //---------------------------------------------------------------------------------------------
  // Implement Remote.events
  networkEventHandler.responseHandlers = {
    Network: {
        dataReceived: function(requestId, timestamp, dataLength, encodedDataLength){
          var resource = networkEventHandler.getRequestById(requestId);
          resource.progress = resource.progress || [];
          resource.progress.push({timestamp: timestamp, dataLength: dataLength, encodedDataLength: encodedDataLength});
        },
        loadingFailed: function(requestId, timestamp, errorText, canceled){
          var resource = networkEventHandler.getRequestById(requestId);
          resource.timestamps.loadingFailed = timestamp;
          resource.errorText = errorText;
          resource.canceled = canceled;
        },
        loadingFinished: function(requestId, timestamp){
          var resource = networkEventHandler.getRequestById(requestId);
          resource.timestamps.loadingFailed = timestamp;
        },
        requestServedFromCache: function(requestId){
          var resource = networkEventHandler.getRequestById(requestId);
          resource.servedFromCache = true;
        },
        requestServedFromMemoryCache: function(requestId, loaderId, documentURL, timestamp, initiator, cachedResource){
          var url = cachedResource.url;
          var resource = networkEventHandler.getOrCreateResource(url);
          resource.documentURL = documentURL;
          resource.requestId = requestId;
          resource.loaderId = loaderId;
          resource.timestamps = {"fromMemoryCache": timestamp};
          resource.initiator = initiator;
          resource.resource = cachedResource;
          networkEventHandler.setRequestById(requestId, resource);
          networkEventHandler.logger(resource);
        },
        requestWillBeSent: function(requestId, loaderId, documentURL, request, timestamp, initiator, stackTrace, redirectResponse){
          var url = request.url;
          var resource = networkEventHandler.getOrCreateResource(url);
          resource.documentURL = documentURL;
          resource.requestId = requestId;
          resource.loaderId = loaderId;
          resource.request = request;
          resource.timestamps = {"sent": timestamp};
          resource.initiator = initiator;
          resource.stackTrace = stackTrace;
          resource.redirectResponse = redirectResponse;
          networkEventHandler.setRequestById(requestId, resource);
          networkEventHandler.logger(resource);
        },
        responseRecieved: function(requestId, timestamp, type, response){
          var resource = networkEventHandler.getRequestById(requestId);
          resource.timestamps.responseRecieved = timestamp;
          resource.type = type;
          resource.response = response;
        }
      },
      WebNavigation: {
        onBeforeNavigate: function(details){
          networkEventHandler.logger(details);
        },
        onBeforeRetarget: function(details){
          networkEventHandler.logger(details);
        },
        onCommitted: function(details){
          networkEventHandler.logger(details);
        },
        onCompleted: function(details){
          networkEventHandler.logger(details);
        },
        onDOMContentLoaded: function(details){
          networkEventHandler.logger(details);
        },
        onErrorOccurred: function(details){
          networkEventHandler.logger(details);
        }
      }
  };

   //---------------------------------------------------------------------------------------------
  // Implement PurplePart

  networkEventHandler.connect = function(channel) {
      this.remote = remoteByWebInspector.create('networkRemote', this.responseHandlers);
      this.remote.connect(channel);
      this.logger = channel.recv.bind(channel);
	  this.remote.Network.enable();
  };

  networkEventHandler.disconnect = function(channel) {
      this.remote.Network.disable();
      Resources.disconnect(this.logger);
  };

  thePurple.registerPart(networkEventHandler);

  return networkEventHandler;
});<|endoftext|>


<commit_before>var instasync = require('instasync');
var config = instasync.config;
var helpers = instasync.helpers;
var cloudflare = require('cloudflare-express');
var express = require('express');
var engine = require('ejs-locals');
var path = require('path');
var favicon = require('static-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var lessMiddleware = require('less-middleware');
var fs = require('fs');
var app = express();

var db = instasync.db;
var sanitizeHtml = require('sanitize-html');

//Automaticly create styles.css incase it doesnt exist (nessecary for .less parser)
//because we have it gitignored since its generated at runtime.
fs.appendFileSync("./public/css/styles.css", "");

var routes = {
	pages: require('./routes/pages'),
	rooms: require('./routes/rooms'),
	ajax: require('./routes/ajax'),
	legacy: require('./routes/legacy')
};

app.engine('ejs', engine);
app.disable('etag');
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');

app.use(cloudflare.restore());
app.use(favicon(__dirname + '/public/favicon.ico'));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded());
app.use(cookieParser());
app.use(lessMiddleware(path.join(__dirname, 'public'), {once: config.environment != "dev"}));
app.use(express.static(path.join(__dirname, 'public'), {maxAge: 18000000}));

app.use(helpers.url_formater.removeTrailingSlashes);
app.use(helpers.url_formater.noFileExtensions);

app.use(function(req,res,next){ //remove after converting to helpers.queries
	req.db = db;
	next();
});
app.use(function(req,res,next){ //domain access only
	var host = req.headers.host;
	var hostname = ( req.headers.host.match(/:/g) ) ? req.headers.host.slice( 0, req.headers.host.indexOf(":") ) : req.headers.host;
	if (hostname == "localhost" || host =="instasync.com" || host == "dev.instasync.com"){
		next();
	}
	else{
		res.status(404).send("Invalid Domain.");
	}
});
app.use('/', routes.legacy); //legacy first (redirect old URLs
app.use('/', routes.pages); //fallback to pages first
app.use('/r/', routes.rooms);
app.use('/rooms/', routes.rooms);
app.use('/ajax/', routes.ajax);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
	next({
		status: 404
	});
});
var error_handler = function(err, req, res, next){
	var pageTitle = "InstaSync - ";
	var status = err.status || 500;
	if (status == 404){
		err.message = "File not found.";
		err.stack = "URL : "+req.url+" could not be located on this server.";
		pageTitle = "InstaSync - Page not found";
	}
	else if(status = 500){
		pageTitle = "InstaSync - Server Error";
		if (config.environment != "dev"){
			err.message = "A server error has occured. Please try again later.";
			err.stack = "";
		}
	}
	res.status(status);
	err.status = status;
	res.render('error', {
		error: err,
		title: pageTitle
	});
};
app.use(error_handler);

/*
 * helper functions
 */
app.locals.commaSeparateNumber = function(val){
	while (/(\d+)(\d{3})/.test(val.toString())){
		val = val.toString().replace(/(\d+)(\d{3})/, '$1'+','+'$2');
	}
	return val;
};
app.locals.sanitizeRoomInfo = function(dirty){
	return sanitizeHtml(dirty,{
		transformTags: {
			'a': sanitizeHtml.simpleTransform('a', {target: '_blank'}),
		}
	});
};
module.exports = app;<commit_msg>Removed forcing instasync.com<commit_after>var instasync = require('instasync');
var config = instasync.config;
var helpers = instasync.helpers;
var cloudflare = require('cloudflare-express');
var express = require('express');
var engine = require('ejs-locals');
var path = require('path');
var favicon = require('static-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var lessMiddleware = require('less-middleware');
var fs = require('fs');
var app = express();

var db = instasync.db;
var sanitizeHtml = require('sanitize-html');

//Automaticly create styles.css incase it doesnt exist (nessecary for .less parser)
//because we have it gitignored since its generated at runtime.
fs.appendFileSync("./public/css/styles.css", "");

var routes = {
	pages: require('./routes/pages'),
	rooms: require('./routes/rooms'),
	ajax: require('./routes/ajax'),
	legacy: require('./routes/legacy')
};

app.engine('ejs', engine);
app.disable('etag');
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');

app.use(cloudflare.restore());
app.use(favicon(__dirname + '/public/favicon.ico'));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded());
app.use(cookieParser());
app.use(lessMiddleware(path.join(__dirname, 'public'), {once: config.environment != "dev"}));
app.use(express.static(path.join(__dirname, 'public'), {maxAge: 18000000}));

app.use(helpers.url_formater.removeTrailingSlashes);
app.use(helpers.url_formater.noFileExtensions);

app.use(function(req,res,next){ //remove after converting to helpers.queries
	req.db = db;
	next();
});
app.use(function(req,res,next){ //domain access only
	return next(); //forcing return since instasync.com is no longer used
	var host = req.headers.host;
	var hostname = ( req.headers.host.match(/:/g) ) ? req.headers.host.slice( 0, req.headers.host.indexOf(":") ) : req.headers.host;
	if (hostname == "localhost" || host =="instasync.com" || host == "dev.instasync.com"){
		next();
	}
	else{
		res.status(404).send("Invalid Domain.");
	}
});
app.use('/', routes.legacy); //legacy first (redirect old URLs
app.use('/', routes.pages); //fallback to pages first
app.use('/r/', routes.rooms);
app.use('/rooms/', routes.rooms);
app.use('/ajax/', routes.ajax);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
	next({
		status: 404
	});
});
var error_handler = function(err, req, res, next){
	var pageTitle = "InstaSync - ";
	var status = err.status || 500;
	if (status == 404){
		err.message = "File not found.";
		err.stack = "URL : "+req.url+" could not be located on this server.";
		pageTitle = "InstaSync - Page not found";
	}
	else if(status = 500){
		pageTitle = "InstaSync - Server Error";
		if (config.environment != "dev"){
			err.message = "A server error has occured. Please try again later.";
			err.stack = "";
		}
	}
	res.status(status);
	err.status = status;
	res.render('error', {
		error: err,
		title: pageTitle
	});
};
app.use(error_handler);

/*
 * helper functions
 */
app.locals.commaSeparateNumber = function(val){
	while (/(\d+)(\d{3})/.test(val.toString())){
		val = val.toString().replace(/(\d+)(\d{3})/, '$1'+','+'$2');
	}
	return val;
};
app.locals.sanitizeRoomInfo = function(dirty){
	return sanitizeHtml(dirty,{
		transformTags: {
			'a': sanitizeHtml.simpleTransform('a', {target: '_blank'}),
		}
	});
};
module.exports = app;
<|endoftext|>


<commit_before>define(function() {

  'use strict';

  function open(item) {
    return item.getBytes().then(function(bytes) {
      var dv = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
      var len = dv.getUint32(0, false);
      if (len > bytes.length) {
        return Promise.reject('length does not match data');
      }
      item.playHeadFactory = {
        create: function() {
          return new FramePlayHead(bytes.buffer, bytes.byteOffset + 4, len - 4);
        },
      };
    });
  }

  var BUFFER_BYTES = 1024;
  var SPRITE_BYTES = 16; // 20
  var SPRITE_OFFSET = 32;

  function FramePlayHead(sourceBuffer, sourceByteOffset, sourceByteLength) {
    this.sourceDataView = new DataView(sourceBuffer, sourceByteOffset, sourceByteLength);
    this.sourceBytes = new Uint8Array(sourceBuffer, sourceByteOffset, sourceByteLength);
    this.sourceNextPos = 0;
    this.currentBytes = new Uint8Array(BUFFER_BYTES);
    this.dataView = new DataView(this.currentBytes.buffer, this.currentBytes.byteOffset, SPRITE_OFFSET);
    this.sprites = new Array();
    for (var i = SPRITE_OFFSET; i < BUFFER_BYTES; i += SPRITE_BYTES) {
      this.sprites.push(new SpriteView(this.currentBytes.buffer, this.currentBytes.byteOffset + i, SPRITE_BYTES));
    }
    this.next = this.next.bind(this);
  }
  FramePlayHead.prototype = {
    next: function() {
      var pos = this.sourceNextPos;
      var endPos = pos + this.sourceDataView.getUint16(pos);
      this.sourceNextPos = endPos % this.sourceDataView.byteLength;
      pos += 2;
      var bytes = this.sourceBytes;
      var currentBytes = this.currentBytes;
      var firstByteChanged = Infinity, lastByteChanged = -Infinity;
      while (pos < endPos) {
        var patchLength = bytes[pos] * 2, patchOffset = bytes[pos + 1] * 2;
        pos += 2;
        var patch = bytes.subarray(pos, pos + patchLength);
        pos += patchLength;
        currentBytes.set(patch, patchOffset);
        firstByteChanged = Math.min(firstByteChanged, patchOffset);
        lastByteChanged = Math.max(lastByteChanged, patchOffset + patchLength - 1);
      }
      this.firstByteChanged = firstByteChanged;
      this.lastByteChanged = lastByteChanged;

      this.eventTarget.dispatchEvent(new Event('enter-frame'));

      var remaining = this.duration;
      if (typeof remaining === 'number') {
        window.setTimeout(this.next, remaining);
      }
    },
    eventTarget: window,
    defaultDuration: 1000/15,
    get duration() {
      var v = this.dataView.getInt8(4);
      return (v === 0) ? this.defaultDuration
        : (v >= 1 && v <= 60) ? (1000 / v)
        : (v <= -1 && v >= -30) ? (250 * -v)
        : (v === -128) ? 'untilUserAction'
        : (v >= -122 && v <= -121) ? ('untilAfterSound' + (-120 - v))
        : 'untilAfterVideo' + (v + 121);
    },
    get transition() {
      switch(this.dataView.getUint8(5)) {
        case 0: return null;
        case 1: return 'wipeRight';
        case 2: return 'wipeLeft';
        case 3: return 'wipeDown';
        case 4: return 'wipeUp';
        case 5: return 'centerOutHorizontal';
        case 6: return 'edgesInHorizontal';
        case 7: return 'centerOutVertical';
        case 8: return 'edgesInVertical';
        case 9: return 'centerOutSquare';
        case 10: return 'edgesInSquare';
        case 11: return 'pushLeft';
        case 12: return 'pushRight';
        case 13: return 'pushDown';
        case 14: return 'pushUp';
        case 15: return 'revealUp';
        case 16: return 'revealUpRight';
        case 17: return 'revealRight';
        case 18: return 'revealDownRight';
        case 19: return 'revealDown';
        case 20: return 'revealDownLeft';
        case 21: return 'revealLeft';
        case 22: return 'revealUpLeft';
        case 22: return 'dissolvePixelsFast';
        case 23: return 'dissolveBoxyRectangles';
        case 24: return 'dissolveBoxySquares';
        case 25: return 'dissolvePatterns';
        case 26: return 'randomRows';
        case 27: return 'randomColumns';
        case 28: return 'coverDown';
        case 29: return 'coverDownLeft';
        case 30: return 'coverDownRight';
        case 31: return 'coverLeft';
        case 32: return 'coverRight';
        case 33: return 'coverUp';
        case 34: return 'coverUpLeft';
        case 35: return 'coverUpRight';
        case 36: return 'venetianBlinds';
        case 37: return 'checkerboard';
        case 38: return 'stripsBottomBuildLeft';
        case 39: return 'stripsBottomBuildRight';
        case 40: return 'stripsLeftBuildDown';
        case 41: return 'stripsLeftBuildUp';
        case 42: return 'stripsRightBuildDown';
        case 43: return 'stripsRightBuildUp';
        case 44: return 'stripsTopBuildLeft';
        case 45: return 'stripsTopBuildRight';
        case 46: return 'zoomOpen';
        case 47: return 'zoomClose';
        case 48: return 'verticalBlinds';
        case 49: return 'dissolveBitsFast';
        case 50: return 'dissolvePixels';
        case 51: return 'dissolveBits';
        default: return this.dataView.getUint8(5);
      }
    },
    get audio1() {
      return this.dataView.getUint16(6, false);
    },
    get audio2() {
      return this.dataView.getUint16(8, false);
    },
    get script() {
      return this.dataView.getUint16(16, false);
    },
    get palette() {
      return this.dataView.getInt16(0x14, false);
    },
  };

  function SpriteView(buffer, byteOffset, byteLength) {
    this.dataView = new DataView(buffer, byteOffset, byteLength);
  }
  SpriteView.prototype = {
    toJSON: function() {
      if (!this.cast) return null;
      return {
        cast: this.cast,
        top: this.top,
        left: this.left,
        bottom: this.bottom,
        right: this.right,
        ink: this.ink,
      };
    },
    get ink() {
      var v = this.dataView.getUint8(5) & 0xf;
      switch (v) {
        case 0x00: return 'copy';
        case 0x01: return 'transparent';
        case 0x02: return 'reverse';
        case 0x03: return 'ghost';
        case 0x04: return 'notCopy';
        case 0x05: return 'notTransparent';
        case 0x06: return 'notReverse';
        case 0x07: return 'notGhost';
        case 0x08: return 'matte';
        case 0x09: return 'mask';
        case 0x20: return 'blend';
        case 0x21: return 'addPin';
        case 0x22: return 'add';
        case 0x23: return 'subtractPin';
        case 0x25: return 'lightest';
        case 0x26: return 'subtract';
        case 0x27: return 'darkest';
        default: return v;
      }
    },
    get cast() {
      return this.dataView.getUint16(6, false);
    },
    get top() {
      return this.dataView.getInt16(8, false);
    },
    get left() {
      return this.dataView.getInt16(10, false);
    },
    get bottom() {
      return this.dataView.getInt16(12, false);
    },
    get right() {
      return this.dataView.getInt16(14, false);
    },
    get width() {
      return this.right - this.left;
    },
    get height() {
      return this.bottom - this.top;
    },
  };

  return open;

});
<commit_msg>Update open_VWSC.js<commit_after>define(function() {

  'use strict';

  function open(item) {
    var promise = item.getBytes().then(function(bytes) {
      var dv = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
      var len = dv.getUint32(0, false);
      if (len > bytes.length) {
        return Promise.reject('length does not match data');
      }
      item.playHeadFactory = {
        create: function() {
          return new FramePlayHead(bytes.buffer, bytes.byteOffset + 4, len - 4);
        },
      };
    });
    item.notifyPopulating(promise);
    return promise;
  }

  var BUFFER_BYTES = 1024;
  var SPRITE_BYTES = 16; // 20
  var SPRITE_OFFSET = 32;

  function FramePlayHead(sourceBuffer, sourceByteOffset, sourceByteLength) {
    this.sourceDataView = new DataView(sourceBuffer, sourceByteOffset, sourceByteLength);
    this.sourceBytes = new Uint8Array(sourceBuffer, sourceByteOffset, sourceByteLength);
    this.sourceNextPos = 0;
    this.currentBytes = new Uint8Array(BUFFER_BYTES);
    this.dataView = new DataView(this.currentBytes.buffer, this.currentBytes.byteOffset, SPRITE_OFFSET);
    this.sprites = new Array();
    for (var i = SPRITE_OFFSET; i < BUFFER_BYTES; i += SPRITE_BYTES) {
      this.sprites.push(new SpriteView(this.currentBytes.buffer, this.currentBytes.byteOffset + i, SPRITE_BYTES));
    }
    this.next = this.next.bind(this);
  }
  FramePlayHead.prototype = {
    next: function() {
      var pos = this.sourceNextPos;
      var endPos = pos + this.sourceDataView.getUint16(pos);
      this.sourceNextPos = endPos % this.sourceDataView.byteLength;
      pos += 2;
      var bytes = this.sourceBytes;
      var currentBytes = this.currentBytes;
      var firstByteChanged = Infinity, lastByteChanged = -Infinity;
      while (pos < endPos) {
        var patchLength = bytes[pos] * 2, patchOffset = bytes[pos + 1] * 2;
        pos += 2;
        var patch = bytes.subarray(pos, pos + patchLength);
        pos += patchLength;
        currentBytes.set(patch, patchOffset);
        firstByteChanged = Math.min(firstByteChanged, patchOffset);
        lastByteChanged = Math.max(lastByteChanged, patchOffset + patchLength - 1);
      }
      this.firstByteChanged = firstByteChanged;
      this.lastByteChanged = lastByteChanged;

      this.eventTarget.dispatchEvent(new Event('enter-frame'));

      var remaining = this.duration;
      if (typeof remaining === 'number') {
        window.setTimeout(this.next, remaining);
      }
    },
    eventTarget: window,
    defaultDuration: 1000/15,
    get duration() {
      var v = this.dataView.getInt8(4);
      return (v === 0) ? this.defaultDuration
        : (v >= 1 && v <= 60) ? (1000 / v)
        : (v <= -1 && v >= -30) ? (250 * -v)
        : (v === -128) ? 'untilUserAction'
        : (v >= -122 && v <= -121) ? ('untilAfterSound' + (-120 - v))
        : 'untilAfterVideo' + (v + 121);
    },
    get transition() {
      switch(this.dataView.getUint8(5)) {
        case 0: return null;
        case 1: return 'wipeRight';
        case 2: return 'wipeLeft';
        case 3: return 'wipeDown';
        case 4: return 'wipeUp';
        case 5: return 'centerOutHorizontal';
        case 6: return 'edgesInHorizontal';
        case 7: return 'centerOutVertical';
        case 8: return 'edgesInVertical';
        case 9: return 'centerOutSquare';
        case 10: return 'edgesInSquare';
        case 11: return 'pushLeft';
        case 12: return 'pushRight';
        case 13: return 'pushDown';
        case 14: return 'pushUp';
        case 15: return 'revealUp';
        case 16: return 'revealUpRight';
        case 17: return 'revealRight';
        case 18: return 'revealDownRight';
        case 19: return 'revealDown';
        case 20: return 'revealDownLeft';
        case 21: return 'revealLeft';
        case 22: return 'revealUpLeft';
        case 22: return 'dissolvePixelsFast';
        case 23: return 'dissolveBoxyRectangles';
        case 24: return 'dissolveBoxySquares';
        case 25: return 'dissolvePatterns';
        case 26: return 'randomRows';
        case 27: return 'randomColumns';
        case 28: return 'coverDown';
        case 29: return 'coverDownLeft';
        case 30: return 'coverDownRight';
        case 31: return 'coverLeft';
        case 32: return 'coverRight';
        case 33: return 'coverUp';
        case 34: return 'coverUpLeft';
        case 35: return 'coverUpRight';
        case 36: return 'venetianBlinds';
        case 37: return 'checkerboard';
        case 38: return 'stripsBottomBuildLeft';
        case 39: return 'stripsBottomBuildRight';
        case 40: return 'stripsLeftBuildDown';
        case 41: return 'stripsLeftBuildUp';
        case 42: return 'stripsRightBuildDown';
        case 43: return 'stripsRightBuildUp';
        case 44: return 'stripsTopBuildLeft';
        case 45: return 'stripsTopBuildRight';
        case 46: return 'zoomOpen';
        case 47: return 'zoomClose';
        case 48: return 'verticalBlinds';
        case 49: return 'dissolveBitsFast';
        case 50: return 'dissolvePixels';
        case 51: return 'dissolveBits';
        default: return this.dataView.getUint8(5);
      }
    },
    get audio1() {
      return this.dataView.getUint16(6, false);
    },
    get audio2() {
      return this.dataView.getUint16(8, false);
    },
    get script() {
      return this.dataView.getUint16(16, false);
    },
    get palette() {
      return this.dataView.getInt16(0x14, false);
    },
  };

  function SpriteView(buffer, byteOffset, byteLength) {
    this.dataView = new DataView(buffer, byteOffset, byteLength);
  }
  SpriteView.prototype = {
    toJSON: function() {
      if (!this.cast) return null;
      return {
        cast: this.cast,
        top: this.top,
        left: this.left,
        bottom: this.bottom,
        right: this.right,
        ink: this.ink,
      };
    },
    get ink() {
      var v = this.dataView.getUint8(5) & 0xf;
      switch (v) {
        case 0x00: return 'copy';
        case 0x01: return 'transparent';
        case 0x02: return 'reverse';
        case 0x03: return 'ghost';
        case 0x04: return 'notCopy';
        case 0x05: return 'notTransparent';
        case 0x06: return 'notReverse';
        case 0x07: return 'notGhost';
        case 0x08: return 'matte';
        case 0x09: return 'mask';
        case 0x20: return 'blend';
        case 0x21: return 'addPin';
        case 0x22: return 'add';
        case 0x23: return 'subtractPin';
        case 0x25: return 'lightest';
        case 0x26: return 'subtract';
        case 0x27: return 'darkest';
        default: return v;
      }
    },
    get cast() {
      return this.dataView.getUint16(6, false);
    },
    get top() {
      return this.dataView.getInt16(8, false);
    },
    get left() {
      return this.dataView.getInt16(10, false);
    },
    get bottom() {
      return this.dataView.getInt16(12, false);
    },
    get right() {
      return this.dataView.getInt16(14, false);
    },
    get width() {
      return this.right - this.left;
    },
    get height() {
      return this.bottom - this.top;
    },
  };

  return open;

});
<|endoftext|>


<commit_before>// This is the code from the README.

const demoDatabase = {
  findUser(id, callback) {
    process.nextTick(() => {
      // return result asynchronously
      if (id === 42) {
        callback(null, { name: 'Bob' })
      } else {
        callback(new Error('not found'))
      }
    })
  },
}

function sayHi1(userId, callback) {
  demoDatabase.findUser(userId, (err, user) => {
    if (err) return callback(err)
    callback(null, 'Hi ' + user.name)
  })
}

sayHi1(42, (err, result) => {
  if (err) {
    console.error(err)
  } else {
    console.log(result)
  }
})

sayHi1(43, (err, result) => {
  if (err) {
    console.error(err)
  } else {
    console.log(result)
  }
})

const OError = require('.')

function sayHi2(userId, callback) {
  demoDatabase.findUser(userId, (err, user) => {
    if (err) return callback(OError.tag(err))
    callback(null, 'Hi ' + user.name)
  })
}

sayHi2(43, (err, result) => {
  if (err) {
    console.error(OError.getFullStack(OError.tag(err)))
  } else {
    console.log(result)
  }
})

function sayHi3(userId, callback) {
  demoDatabase.findUser(userId, (err, user) => {
    if (err) return callback(OError.tag(err, 'failed to find user', { userId }))
    callback(null, 'Hi ' + user.name)
  })
}

sayHi3(43, (err, result) => {
  if (err) {
    OError.tag(err, 'failed to say hi')
    console.error(OError.getFullStack(err))
    console.error(OError.getFullInfo(err))
  } else {
    console.log(result)
  }
})

const promisify = require('util').promisify
demoDatabase.findUserAsync = promisify(demoDatabase.findUser)

async function sayHi4NoHandling(userId) {
  const user = await demoDatabase.findUserAsync(userId)
  return `Hi ${user.name}`
}

async function sayHi4(userId) {
  try {
    const user = await demoDatabase.findUserAsync(userId)
    return `Hi ${user.name}`
  } catch (error) {
    throw OError.tag(error, 'failed to find user', { userId })
  }
}

async function main() {
  try {
    await sayHi4NoHandling(43)
  } catch (error) {
    console.error(OError.getFullStack(error))
    console.error(OError.getFullInfo(error))
  }

  try {
    await sayHi4(43)
  } catch (error) {
    OError.tag(error, 'failed to say hi')
    console.error(OError.getFullStack(error))
    console.error(OError.getFullInfo(error))
  }
}
main()

class UserNotFoundError extends OError {
  constructor(userId) {
    super('user not found', { userId })
  }
}

try {
  throw new UserNotFoundError(123)
} catch (error) {
  console.error(OError.getFullStack(error))
  console.error(OError.getFullInfo(error))
}

async function sayHi5(userId) {
  try {
    const user = await demoDatabase.findUserAsync(userId)
    return `Hi ${user.name}`
  } catch (error) {
    if (error.message === 'not found') {
      throw new UserNotFoundError(userId).withCause(error)
    }
  }
}

async function main2() {
  try {
    await sayHi5(43)
  } catch (error) {
    OError.tag(error, 'failed to say hi')
    console.error(OError.getFullStack(error))
    console.error(OError.getFullInfo(error))
  }
}
main2()
<commit_msg>Fix demo require path<commit_after>// This is the code from the README.

const OError = require('..')

const demoDatabase = {
  findUser(id, callback) {
    process.nextTick(() => {
      // return result asynchronously
      if (id === 42) {
        callback(null, { name: 'Bob' })
      } else {
        callback(new Error('not found'))
      }
    })
  },
}

function sayHi1(userId, callback) {
  demoDatabase.findUser(userId, (err, user) => {
    if (err) return callback(err)
    callback(null, 'Hi ' + user.name)
  })
}

sayHi1(42, (err, result) => {
  if (err) {
    console.error(err)
  } else {
    console.log(result)
  }
})

sayHi1(43, (err, result) => {
  if (err) {
    console.error(err)
  } else {
    console.log(result)
  }
})

function sayHi2(userId, callback) {
  demoDatabase.findUser(userId, (err, user) => {
    if (err) return callback(OError.tag(err))
    callback(null, 'Hi ' + user.name)
  })
}

sayHi2(43, (err, result) => {
  if (err) {
    console.error(OError.getFullStack(OError.tag(err)))
  } else {
    console.log(result)
  }
})

function sayHi3(userId, callback) {
  demoDatabase.findUser(userId, (err, user) => {
    if (err) return callback(OError.tag(err, 'failed to find user', { userId }))
    callback(null, 'Hi ' + user.name)
  })
}

sayHi3(43, (err, result) => {
  if (err) {
    OError.tag(err, 'failed to say hi')
    console.error(OError.getFullStack(err))
    console.error(OError.getFullInfo(err))
  } else {
    console.log(result)
  }
})

const promisify = require('util').promisify
demoDatabase.findUserAsync = promisify(demoDatabase.findUser)

async function sayHi4NoHandling(userId) {
  const user = await demoDatabase.findUserAsync(userId)
  return `Hi ${user.name}`
}

async function sayHi4(userId) {
  try {
    const user = await demoDatabase.findUserAsync(userId)
    return `Hi ${user.name}`
  } catch (error) {
    throw OError.tag(error, 'failed to find user', { userId })
  }
}

async function main() {
  try {
    await sayHi4NoHandling(43)
  } catch (error) {
    console.error(OError.getFullStack(error))
    console.error(OError.getFullInfo(error))
  }

  try {
    await sayHi4(43)
  } catch (error) {
    OError.tag(error, 'failed to say hi')
    console.error(OError.getFullStack(error))
    console.error(OError.getFullInfo(error))
  }
}
main()

class UserNotFoundError extends OError {
  constructor(userId) {
    super('user not found', { userId })
  }
}

try {
  throw new UserNotFoundError(123)
} catch (error) {
  console.error(OError.getFullStack(error))
  console.error(OError.getFullInfo(error))
}

async function sayHi5(userId) {
  try {
    const user = await demoDatabase.findUserAsync(userId)
    return `Hi ${user.name}`
  } catch (error) {
    if (error.message === 'not found') {
      throw new UserNotFoundError(userId).withCause(error)
    }
  }
}

async function main2() {
  try {
    await sayHi5(43)
  } catch (error) {
    OError.tag(error, 'failed to say hi')
    console.error(OError.getFullStack(error))
    console.error(OError.getFullInfo(error))
  }
}
main2()
<|endoftext|>


<commit_before>/**
 * @class CM_Layout_Abstract
 * @extends CM_View_Abstract
 */
var CM_Layout_Abstract = CM_View_Abstract.extend({

  /** @type String */
  _class: 'CM_Layout_Abstract',

  /** @type jQuery|Null */
  _$pagePlaceholder: null,

  /** @type PromiseThrottled|Null */
  _loadPageThrottled: null,

  /** @type {Number} timeout ID */
  _timeoutLoading: null,

  initialize: function() {
    CM_View_Abstract.prototype.initialize.call(this);

    var layout = this;
    this._loadPageThrottled = promiseThrottler(function(path) {
      return layout.ajaxModal('loadPage', {path: path})
        .then(function(response) {
          window.clearTimeout(layout._timeoutLoading);
          if (response.redirectExternal) {
            cm.router.route(response.redirectExternal);
            return;
          }
          var view = layout._injectView(response);
          var reload = (layout.getClass() != response.layoutClass);
          if (reload) {
            window.location.replace(response.url);
            return;
          }
          layout._$pagePlaceholder.replaceWith(view.$el);
          layout._$pagePlaceholder = null;
          var fragment = response.url.substr(cm.getUrl().length);
          if (path === fragment + window.location.hash) {
            fragment = path;
          }
          window.history.replaceState(null, null, fragment);
          layout._onPageSetup(view, response.title, response.url, response.menuEntryHashList, response.jsTracking);
          view._ready();
          return view;
        })
        .catch(function(error) {
          window.clearTimeout(layout._timeoutLoading);
          if (!(error instanceof Promise.CancellationError)) {
            layout._$pagePlaceholder.addClass('error').html('
' + error.message + '
');
            layout._onPageError();
            throw error;
          }
        });
    }, {cancelLeading: true});
  },

  /**
   * @returns {CM_View_Abstract|null}
   */
  findPage: function() {
    return this.findChild('CM_Page_Abstract');
  },

  /**
   * @returns {CM_View_Abstract}
   */
  getPage: function() {
    var page = this.findPage();
    if (!page) {
      throw new CM_Exception('Layout doesn\'t have a page');
    }
    return page;
  },

  /**
   * @param {String} path
   * @return Promise
   */
  loadPage: function(path) {
    cm.event.trigger('navigate', path);

    if (!this._$pagePlaceholder) {
      this._$pagePlaceholder = $('
');
      this.getPage().replaceWithHtml(this._$pagePlaceholder);
      this._onPageTeardown();
    } else {
      this._$pagePlaceholder.removeClass('error').html('');
    }
    this._timeoutLoading = this.setTimeout(function() {
      this._$pagePlaceholder.html('
');
    }, 750);
    return this._loadPageThrottled(path);
  },

  /**
   * @param {jQuery} $el
   */
  scrollTo: function($el) {
    var pageOffsetTop = 0;
    var page = cm.findView('CM_Page_Abstract');
    if (page) {
      pageOffsetTop = page.$el.offset().top;
    }
    $(document).scrollTop($el.offset().top - pageOffsetTop);
  },

  _onPageTeardown: function() {
    $(document).scrollTop(0);
    $('.floatbox-layer').floatIn();
  },

  /**
   * @param {CM_Page_Abstract} page
   * @param {String} title
   * @param {String} url
   * @param {String[]} menuEntryHashList
   * @param {String} [jsTracking]
   */
  _onPageSetup: function(page, title, url, menuEntryHashList, jsTracking) {
    cm.window.title.setText(title);
    $('[data-menu-entry-hash]').removeClass('active');
    var menuEntrySelectors = _.map(menuEntryHashList, function(menuEntryHash) {
      return '[data-menu-entry-hash=' + menuEntryHash + ']';
    });
    $(menuEntrySelectors.join(',')).addClass('active');
    if (jsTracking) {
      new Function(jsTracking).call(this);
    }
    if (window.location.hash) {
      var hash = window.location.hash.substring(1);
      var $anchor = $('#' + hash).add('[name=' + hash + ']');
      if ($anchor.length) {
        this.scrollTo($anchor);
      }
    }
  },

  _onPageError: function() {
    $('[data-menu-entry-hash]').removeClass('active');
  }
});
<commit_msg>Use that promise-throttler passes context.<commit_after>/**
 * @class CM_Layout_Abstract
 * @extends CM_View_Abstract
 */
var CM_Layout_Abstract = CM_View_Abstract.extend({

  /** @type String */
  _class: 'CM_Layout_Abstract',

  /** @type jQuery|Null */
  _$pagePlaceholder: null,

  /** @type PromiseThrottled|Null */
  _loadPageThrottled: promiseThrottler(function(path) {
    var layout = this;
    return this.ajaxModal('loadPage', {path: path})
      .then(function(response) {
        window.clearTimeout(layout._timeoutLoading);
        if (response.redirectExternal) {
          cm.router.route(response.redirectExternal);
          return;
        }
        var view = layout._injectView(response);
        var reload = (layout.getClass() != response.layoutClass);
        if (reload) {
          window.location.replace(response.url);
          return;
        }
        layout._$pagePlaceholder.replaceWith(view.$el);
        layout._$pagePlaceholder = null;
        var fragment = response.url.substr(cm.getUrl().length);
        if (path === fragment + window.location.hash) {
          fragment = path;
        }
        window.history.replaceState(null, null, fragment);
        layout._onPageSetup(view, response.title, response.url, response.menuEntryHashList, response.jsTracking);
        view._ready();
        return view;
      })
      .catch(function(error) {
        window.clearTimeout(layout._timeoutLoading);
        if (!(error instanceof Promise.CancellationError)) {
          layout._$pagePlaceholder.addClass('error').html('
' + error.message + '
');
          layout._onPageError();
          throw error;
        }
      });
  }, {cancelLeading: true}),

  /** @type {Number} timeout ID */
  _timeoutLoading: null,

  /**
   * @returns {CM_View_Abstract|null}
   */
  findPage: function() {
    return this.findChild('CM_Page_Abstract');
  },

  /**
   * @returns {CM_View_Abstract}
   */
  getPage: function() {
    var page = this.findPage();
    if (!page) {
      throw new CM_Exception('Layout doesn\'t have a page');
    }
    return page;
  },

  /**
   * @param {String} path
   * @return Promise
   */
  loadPage: function(path) {
    cm.event.trigger('navigate', path);

    if (!this._$pagePlaceholder) {
      this._$pagePlaceholder = $('
');
      this.getPage().replaceWithHtml(this._$pagePlaceholder);
      this._onPageTeardown();
    } else {
      this._$pagePlaceholder.removeClass('error').html('');
    }
    this._timeoutLoading = this.setTimeout(function() {
      this._$pagePlaceholder.html('
');
    }, 750);
    return this._loadPageThrottled(path);
  },

  /**
   * @param {jQuery} $el
   */
  scrollTo: function($el) {
    var pageOffsetTop = 0;
    var page = cm.findView('CM_Page_Abstract');
    if (page) {
      pageOffsetTop = page.$el.offset().top;
    }
    $(document).scrollTop($el.offset().top - pageOffsetTop);
  },

  _onPageTeardown: function() {
    $(document).scrollTop(0);
    $('.floatbox-layer').floatIn();
  },

  /**
   * @param {CM_Page_Abstract} page
   * @param {String} title
   * @param {String} url
   * @param {String[]} menuEntryHashList
   * @param {String} [jsTracking]
   */
  _onPageSetup: function(page, title, url, menuEntryHashList, jsTracking) {
    cm.window.title.setText(title);
    $('[data-menu-entry-hash]').removeClass('active');
    var menuEntrySelectors = _.map(menuEntryHashList, function(menuEntryHash) {
      return '[data-menu-entry-hash=' + menuEntryHash + ']';
    });
    $(menuEntrySelectors.join(',')).addClass('active');
    if (jsTracking) {
      new Function(jsTracking).call(this);
    }
    if (window.location.hash) {
      var hash = window.location.hash.substring(1);
      var $anchor = $('#' + hash).add('[name=' + hash + ']');
      if ($anchor.length) {
        this.scrollTo($anchor);
      }
    }
  },

  _onPageError: function() {
    $('[data-menu-entry-hash]').removeClass('active');
  }
});
<|endoftext|>


<commit_before>seajs.use([
  '$',
  'placeholder',
  'sticky',
  'word-color',
  'autocomplete',
  'keymaster',
  'moment',
  "/package.json"],
  function($, Placeholder, Sticky, wordColor, Autocomplete, key, moment, package) {

  Sticky.stick('#document-wrapper', 0);

  var modules = [];
  var deprecatedModules = package['module-tags'].deprecated.join(' ');

  var urls = [
    'http://spmjs.org/repository/arale/?define',
    'http://spmjs.org/repository/gallery/?define',
    'http://spmjs.org/repository/jquery/?define'
  ];

  seajs.use(urls, function(arale, gallery, jquery) {
    $('.modules-utility').empty();
    modules = modules.concat(arale);
    modules = modules.concat(gallery);
    modules = modules.concat(jquery);

    insertModules(arale);
    insertModules(gallery);
    insertModules(jquery);
    color('.module');
  });

  seajs.use('http://yuan.alipay.im/repository/alipay/?define', function(alipay) {
    if (alipay) {
      $('.side-area li:last-child').show();
      modules = modules.concat(alipay);
      insertModules(alipay);
      color('.module');
    }
  });

  function insertModules(data) {

    if ($('#module-wrapper').length === 0) {
      return;
    }

    data = data.sort(function(a, b) {
      return a.name.charCodeAt(0) - b.name.charCodeAt(0);
    });

    for (var i = 0; i < data.length; i++) {
      var item = $('
\
        \
        \

\

');

      var pkg = data[i];
      var family = pkg.family || pkg.root;

      // 三十天内更新的标注为新模块
      if (moment().diff(moment(pkg.created_at), 'days') <= 30) {
        item.addClass('module-new');
        item.attr('title', "新模块");
      }

      // 增加 deprecated 信息

      var family_name = family + '/' + pkg.name;
      if (deprecatedModules.indexOf(family_name) >= 0) {
        item.addClass('module-deprecated');
        item.attr('title', "即将废弃的模块");
      }

      item.find(".module-name").html(pkg.name)
                               .attr('href', '/' + pkg.name + '/')
                               .attr('title', pkg.name);
      item.find(".module-version").html(pkg.version);
      item.find(".module-description").html(pkg.description)
                                      .attr('title', pkg.description);

      if (family === 'gallery' || family === 'jquery') {
        item.find(".module-name").attr('href', "https://spmjs.org/" + family + '/' + pkg.name);
        $('.modules-' + family).append(item).prev().show();
      } else if (family === 'arale') {
        if (pkg.keywords) {
          $('.modules-' + pkg.keywords[0]).append(item).prev().show();
        } else {
          $('.modules-widget').append(item).prev().show();
        }
      } else if (family === 'alipay') {
        var url = [
          'http://arale.alipay.im',
          (pkg.family || pkg.root),
          pkg.name
        ].join('/') + '/';
        item.find(".module-name").attr('href', url);
        $('.modules-alipay').append(item).prev().show();
      }
    }
  }

  function color(items) {
    items = $(items);
    items.each(function(index, item) {
      item = $(item);
      item.css('border-left-color', toRgba(wordColor(item.find('.module-name').html()), 0.65));
    });
  }

  function toRgba(rgb, opacity) {
    if (!$.support.opacity) {
      return rgb;
    }
    return rgb.replace('rgb', 'rgba').replace(')', ',' + opacity + ')');
  }

  var ac = new Autocomplete({
    trigger: '#search',
    selectFirst: true,
    template:
        '
\

\
                {{#each items}}\

\

{{highlightItem ../classPrefix matchKey}}
\

{{desc}}
\

\
                {{/each}}\

\

',
    dataSource: function() {
      this.trigger('data', modules);
    },
    filter: function(data, query) {
      var result = [];
      query = query.toLowerCase().replace(/^\s+|\s+$/g, '');

      $.each(data, function(index, value) {
        value.description = (value.description || '').toLowerCase();
        value.family = value.family.toLowerCase();
        value.name = value.name.toLowerCase();
        var FamilyAndName = value.family + '.' + value.name;
        var keywords = value.keywords ? value.keywords.join(' ') : '';

        var item = {
          matchKey: FamilyAndName,
          desc: value.description,
          url: value.homepage,
          score: 0  //匹配度
        };

        // make sure that "arale.class" can be matched
        if (FamilyAndName.indexOf(query) > -1) {
          item.score += 0.01;
        }

        if (value.family.indexOf(query) > -1) {
          item.score += 1;
        }
        if (value.family.indexOf(query) === 0) {
          item.score += 10;
        }

        if (value.name.indexOf(query) > -1) {
          item.score += 20;
          item.score -= value.name.length/0.99; // shorter would be better
        } else if (value.description.indexOf(query) > -1) {
          item.score += 0.1;
        }

        if (keywords.indexOf(query) > -1) {
          item.score += 1;
        }

        value.keywords && value.keywords.forEach(function(k) {
          if (k === query && k !== value.name) {
            item.score += 5;
          }
        });

        if (value.name.indexOf(query) === 0) {
          item.score += 100;
        }

        if (item.score > 0) {
          result.push(item);
        }

      });

      result = result.sort(function(a, b) {
        return b.score - a.score;
      });

      return result.slice(0, 12);

    }
  }).render();

  ac.on('itemSelect', function(item) {
    ac.get('trigger').val('正转到 ' + item.matchKey).attr('disabled', 'disabled');
    var value = item.matchKey.split('.');
    if (value[0] === 'arale') {
      location.href = '/' + value[1];
    } else if (value[0] === 'alipay') {
      location.href = 'http://arale.alipay.im/alipay/' + value[1];
    } else {
      location.href = item.url;
    }
  });

  // 首页将搜索功能定位到搜索框中
  if ($('#module-wrapper').length !== 0) {
    key.filter = function(event) {
      return (event.target || event.srcElement).tagName;
    };
    key('command+f, ctrl+f', function(e, handler) {
      $('#search').focus();
      return false;
    });
    key('esc', function(e, handler) {
      $('#search').blur();
    });
  }

});
<commit_msg>spmjs<commit_after>seajs.use([
  '$',
  'placeholder',
  'sticky',
  'word-color',
  'autocomplete',
  'keymaster',
  'moment',
  "/package.json"],
  function($, Placeholder, Sticky, wordColor, Autocomplete, key, moment, package) {

  Sticky.stick('#document-wrapper', 0);

  var modules = [];
  var deprecatedModules = package['module-tags'].deprecated.join(' ');

  var urls = [
    'http://spmjs.org/repository/arale/?define',
    'http://spmjs.org/repository/gallery/?define',
    'http://spmjs.org/repository/jquery/?define'
  ];

  seajs.use(urls, function(arale, gallery, jquery) {
    $('.modules-utility').empty();
    modules = modules.concat(arale);
    modules = modules.concat(gallery);
    modules = modules.concat(jquery);

    insertModules(arale);
    insertModules(gallery);
    insertModules(jquery);
    color('.module');
  });

  seajs.use('http://yuan.alipay.im/repository/alipay/?define', function(alipay) {
    if (alipay) {
      $('.side-area li:last-child').show();
      modules = modules.concat(alipay);
      insertModules(alipay);
      color('.module');
    }
  });

  function insertModules(data) {

    if ($('#module-wrapper').length === 0) {
      return;
    }

    data = data.sort(function(a, b) {
      return a.name.charCodeAt(0) - b.name.charCodeAt(0);
    });

    for (var i = 0; i < data.length; i++) {
      var item = $('
\
        \
        \

\

');

      var pkg = data[i];
      var family = pkg.family || pkg.root;

      // 三十天内更新的标注为新模块
      if (moment().diff(moment(pkg.created_at), 'days') <= 30) {
        item.addClass('module-new');
        item.attr('title', "新模块");
      }

      // 增加 deprecated 信息

      var family_name = family + '/' + pkg.name;
      if (deprecatedModules.indexOf(family_name) >= 0) {
        item.addClass('module-deprecated');
        item.attr('title', "即将废弃的模块");
      }

      item.find(".module-name").html(pkg.name)
                               .attr('href', '/' + pkg.name + '/')
                               .attr('title', pkg.name);
      item.find(".module-version").html(pkg.version);
      item.find(".module-description").html(pkg.description)
                                      .attr('title', pkg.description);

      if (family === 'gallery' || family === 'jquery') {
        item.find(".module-name").attr('href', "https://spmjs.org/" + family + '/' + pkg.name);
        $('.modules-' + family).append(item).prev().show();
      } else if (family === 'arale') {
        if (pkg.keywords) {
          $('.modules-' + pkg.keywords[0]).append(item).prev().show();
        } else {
          $('.modules-widget').append(item).prev().show();
        }
      } else if (family === 'alipay') {
        var url = [
          'http://arale.alipay.im',
          (pkg.family || pkg.root),
          pkg.name
        ].join('/') + '/';
        item.find(".module-name").attr('href', url);
        $('.modules-alipay').append(item).prev().show();
      }
    }
  }

  function color(items) {
    items = $(items);
    items.each(function(index, item) {
      item = $(item);
      item.css('border-left-color', toRgba(wordColor(item.find('.module-name').html()), 0.65));
    });
  }

  function toRgba(rgb, opacity) {
    if (!$.support.opacity) {
      return rgb;
    }
    return rgb.replace('rgb', 'rgba').replace(')', ',' + opacity + ')');
  }

  var ac = new Autocomplete({
    trigger: '#search',
    selectFirst: true,
    template:
        '
\

\
                {{#each items}}\

\

{{highlightItem ../classPrefix matchKey}}
\

{{desc}}
\

\
                {{/each}}\

\

',
    dataSource: function() {
      this.trigger('data', modules);
    },
    filter: function(data, query) {
      var result = [];
      query = query.toLowerCase().replace(/^\s+|\s+$/g, '');

      $.each(data, function(index, value) {
        value.description = (value.description || '').toLowerCase();
        value.family = value.family.toLowerCase();
        value.name = value.name.toLowerCase();
        var FamilyAndName = value.family + '.' + value.name;
        var keywords = value.keywords ? value.keywords.join(' ') : '';

        var item = {
          matchKey: FamilyAndName,
          desc: value.description,
          url: "https://spmjs.org/" + value.family + '/' + value.name,
          score: 0  //匹配度
        };

        // make sure that "arale.class" can be matched
        if (FamilyAndName.indexOf(query) > -1) {
          item.score += 0.01;
        }

        if (value.family.indexOf(query) > -1) {
          item.score += 1;
        }
        if (value.family.indexOf(query) === 0) {
          item.score += 10;
        }

        if (value.name.indexOf(query) > -1) {
          item.score += 20;
          item.score -= value.name.length/0.99; // shorter would be better
        } else if (value.description.indexOf(query) > -1) {
          item.score += 0.1;
        }

        if (keywords.indexOf(query) > -1) {
          item.score += 1;
        }

        value.keywords && value.keywords.forEach(function(k) {
          if (k === query && k !== value.name) {
            item.score += 5;
          }
        });

        if (value.name.indexOf(query) === 0) {
          item.score += 100;
        }

        if (item.score > 0) {
          result.push(item);
        }

      });

      result = result.sort(function(a, b) {
        return b.score - a.score;
      });

      return result.slice(0, 12);

    }
  }).render();

  ac.on('itemSelect', function(item) {
    ac.get('trigger').val('正转到 ' + item.matchKey).attr('disabled', 'disabled');
    var value = item.matchKey.split('.');
    if (value[0] === 'arale') {
      location.href = '/' + value[1];
    } else if (value[0] === 'alipay') {
      location.href = 'http://arale.alipay.im/alipay/' + value[1];
    } else {
      location.href = item.url;
    }
  });

  // 首页将搜索功能定位到搜索框中
  if ($('#module-wrapper').length !== 0) {
    key.filter = function(event) {
      return (event.target || event.srcElement).tagName;
    };
    key('command+f, ctrl+f', function(e, handler) {
      $('#search').focus();
      return false;
    });
    key('esc', function(e, handler) {
      $('#search').blur();
    });
  }

});
<|endoftext|>