<commit_before>
/**
 * Basic auth configuration.
 */
const googleClientId = '527715214680-3eqom1v7vv9s7oeh9frb7ee2ommr2t88.apps.googleusercontent.com'
const googleLoginType = 'accounts.google.com'
const identityPoolId = 'eu-west-2:515f3296-8235-40d1-99a6-49716ed1328a'
const identityPoolRegion = 'eu-west-2'
const apiGatewayUrl = 'https://bw4dok4cae.execute-api.eu-west-2.amazonaws.com/prod'
const apiGatewayRegion = 'eu-west-2'

/**
 * Add listeners for authentication actions.
 */
chrome.runtime.onConnect.addListener((port) => {
  if (port.name !== 'auth') {
    return
  }

  port.onMessage.addListener(async (request) => {
    if (request.action === 'login') {
      return Auth.login()
        .then(session => port.postMessage({ ok: true, session }))
        .catch(error => port.postMessage({ ok: false, error }))
    }

    if (request.action === 'logout') {
      return Auth.logout()
        .then(() => port.postMessage({ ok: true }))
        .catch(error => port.postMessage({ ok: false, error }))
    }

    if (request.action === 'session') {
      return Auth.session()
        .then(session => port.postMessage({ ok: true, session }))
        .catch(error => port.postMessage({ ok: false, error }))
    }
  })
})

/**
 * Cognito syntax sugar.
 */
const Auth = {

  /**
   * Log the user in.
   *
   * @return {Promise<any>} The user object.
   */
  async login () {
    // Authenticate the user and save the session data to the sync storage.
    await this._authenticate()

    // Create cognito identity credentials.
    await this.credentials()

    // Fire an event and return the session data.
    const session = await this.session()

    this.onLogin.dispatch(session)

    return session
  },

  /**
   * Terminate the session.
   */
  async logout () {
    // Destroy the session stored in the storage.
    return this._destroySession()
  },

  /**
   * Retrieve the session data from the sync storage.
   *
   * @return {Promise<any>} The session data
   */
  async session () {
    return new Promise((resolve, reject) => {
      chrome.storage.sync.get('auth.session', item => item['auth.session']
        ? resolve(item['auth.session'])
        : reject('No user in session')
      )
    })
      .then((session) => new Promise((resolve, reject) => {
        AWS.config.region = identityPoolRegion
        AWS.config.credentials = new AWS.CognitoIdentityCredentials({
          IdentityPoolId: identityPoolId,
          Logins: { [googleLoginType]: session.idToken },
        })

        AWS.config.credentials.get(e => e ? reject(e) : resolve(session))
      }))
  },

  /**
   * Get the coginto idendity session.
   *
   * @return {Promise<any>} The credentials object
   */
  async credentials () {
    return this.session()
      .then((session) => new Promise((resolve) => {
        AWS.config.region = identityPoolRegion
        AWS.config.credentials = new AWS.CognitoIdentityCredentials({
          IdentityPoolId: identityPoolId,
          Logins: { [googleLoginType]: session.idToken },
        })

        AWS.config.credentials.get(() => resolve(AWS.config.credentials))
      }))
  },

  /**
   * Make an authenticated request to the API Gateway.
   *
   * @param {string} method The request method
   * @param {string} path The path
   * @param {any} body The request body
   * @param {object} queryParams Query parameters
   * @param {object} headers The request headers
   * @return {Promise<any>} The response
   */
  async request (method, path, body, queryParams = {}, headers = {}) {
    const credentials = await this.credentials()

    // Sign the request.
    const request = sigV4Client
      .newClient({
        accessKey: credentials.accessKeyId,
        secretKey: credentials.secretAccessKey,
        sessionToken: credentials.sessionToken,
        region: apiGatewayRegion,
        endpoint: apiGatewayUrl
      })
      .signRequest({
        method: method.toUpperCase(),
        path,
        queryParams,
        body,
        headers
      })

    // Send the signed request.
    return axios.request({
      url: request.url,
      method,
      headers: request.headers,
      data: body
    }).then(({ data }) => data)
  },

  /**
   * Initialize the user session via google apis.
   */
  async _authenticate () {
    return new Promise((resolve, reject) => {
      const url = new URL('https://accounts.google.com/o/oauth2/auth')

      url.searchParams.set('client_id', googleClientId)
      url.searchParams.set('redirect_uri', chrome.identity.getRedirectURL('oauth2'))
      url.searchParams.set('response_type', 'id_token token')
      url.searchParams.set('access_type', 'online')
      url.searchParams.set('scope', 'openid profile email')

      // Launch the google oauth flow.
      chrome.identity.launchWebAuthFlow({
        url: url.toString(),
        interactive: true
      }, async (response) => {
        if (chrome.runtime.lastError) {
          reject('Failed to launch the authentication flow')
        }

        // Should reject the promise automatically if anything goes wrong.
        const idToken = new URL(response).hash.match(/id_token=([^&]+)/)[1]
        const accessToken = new URL(response).hash.match(/access_token=([^&]+)/)[1]
        const profile = await axios.get('https://www.googleapis.com/oauth2/v3/userinfo', {
          headers: { Authorization: `Bearer ${accessToken}` }
        }).then(({ data }) => data)

        // Save the session data to the storage.
        return this._saveSession({
          idToken,
          accessToken,
          profile,
        }).then(resolve)
      })
    })
  },

  /**
   * Save the sesion data to the sync storage.
   *
   * @param {any} session The session data.
   */
  async _saveSession (session) {
    return new Promise((resolve) => {
      chrome.storage.sync.set({ 'auth.session': session }, () => resolve())
    })
  },

  /**
   * Destroy the session.
   */
  async _destroySession () {
    return new Promise((resolve) => {
      chrome.storage.sync.remove('auth.session', () => resolve())
    })
  },

  /**
   * Events are defined here.
   */
  onLogin: new Event(),

}
<commit_msg>updated googleClientId<commit_after>
/**
 * Basic auth configuration.
 */
// const googleClientId = '527715214680-3eqom1v7vv9s7oeh9frb7ee2ommr2t88.apps.googleusercontent.com'
const googleClientId = '959526320490-n8sju47gtn52m7tidvbs285qb9vv3cmu.apps.googleusercontent.com'
const googleLoginType = 'accounts.google.com'
const identityPoolId = 'eu-west-2:515f3296-8235-40d1-99a6-49716ed1328a'
const identityPoolRegion = 'eu-west-2'
const apiGatewayUrl = 'https://bw4dok4cae.execute-api.eu-west-2.amazonaws.com/prod'
const apiGatewayRegion = 'eu-west-2'

/**
 * Add listeners for authentication actions.
 */
chrome.runtime.onConnect.addListener((port) => {
  if (port.name !== 'auth') {
    return
  }

  port.onMessage.addListener(async (request) => {
    if (request.action === 'login') {
      return Auth.login()
        .then(session => port.postMessage({ ok: true, session }))
        .catch(error => port.postMessage({ ok: false, error }))
    }

    if (request.action === 'logout') {
      return Auth.logout()
        .then(() => port.postMessage({ ok: true }))
        .catch(error => port.postMessage({ ok: false, error }))
    }

    if (request.action === 'session') {
      return Auth.session()
        .then(session => port.postMessage({ ok: true, session }))
        .catch(error => port.postMessage({ ok: false, error }))
    }
  })
})

/**
 * Cognito syntax sugar.
 */
const Auth = {

  /**
   * Log the user in.
   *
   * @return {Promise<any>} The user object.
   */
  async login () {
    // Authenticate the user and save the session data to the sync storage.
    await this._authenticate()

    // Create cognito identity credentials.
    await this.credentials()

    // Fire an event and return the session data.
    const session = await this.session()

    this.onLogin.dispatch(session)

    return session
  },

  /**
   * Terminate the session.
   */
  async logout () {
    // Destroy the session stored in the storage.
    return this._destroySession()
  },

  /**
   * Retrieve the session data from the sync storage.
   *
   * @return {Promise<any>} The session data
   */
  async session () {
    return new Promise((resolve, reject) => {
      chrome.storage.sync.get('auth.session', item => item['auth.session']
        ? resolve(item['auth.session'])
        : reject('No user in session')
      )
    })
      .then((session) => new Promise((resolve, reject) => {
        AWS.config.region = identityPoolRegion
        AWS.config.credentials = new AWS.CognitoIdentityCredentials({
          IdentityPoolId: identityPoolId,
          Logins: { [googleLoginType]: session.idToken },
        })

        AWS.config.credentials.get(e => e ? reject(e) : resolve(session))
      }))
  },

  /**
   * Get the coginto idendity session.
   *
   * @return {Promise<any>} The credentials object
   */
  async credentials () {
    return this.session()
      .then((session) => new Promise((resolve) => {
        AWS.config.region = identityPoolRegion
        AWS.config.credentials = new AWS.CognitoIdentityCredentials({
          IdentityPoolId: identityPoolId,
          Logins: { [googleLoginType]: session.idToken },
        })

        AWS.config.credentials.get(() => resolve(AWS.config.credentials))
      }))
  },

  /**
   * Make an authenticated request to the API Gateway.
   *
   * @param {string} method The request method
   * @param {string} path The path
   * @param {any} body The request body
   * @param {object} queryParams Query parameters
   * @param {object} headers The request headers
   * @return {Promise<any>} The response
   */
  async request (method, path, body, queryParams = {}, headers = {}) {
    const credentials = await this.credentials()

    // Sign the request.
    const request = sigV4Client
      .newClient({
        accessKey: credentials.accessKeyId,
        secretKey: credentials.secretAccessKey,
        sessionToken: credentials.sessionToken,
        region: apiGatewayRegion,
        endpoint: apiGatewayUrl
      })
      .signRequest({
        method: method.toUpperCase(),
        path,
        queryParams,
        body,
        headers
      })

    // Send the signed request.
    return axios.request({
      url: request.url,
      method,
      headers: request.headers,
      data: body
    }).then(({ data }) => data)
  },

  /**
   * Initialize the user session via google apis.
   */
  async _authenticate () {
    return new Promise((resolve, reject) => {
      const url = new URL('https://accounts.google.com/o/oauth2/auth')

      url.searchParams.set('client_id', googleClientId)
      url.searchParams.set('redirect_uri', chrome.identity.getRedirectURL('oauth2'))
      url.searchParams.set('response_type', 'id_token token')
      url.searchParams.set('access_type', 'online')
      url.searchParams.set('scope', 'openid profile email')

      // Launch the google oauth flow.
      chrome.identity.launchWebAuthFlow({
        url: url.toString(),
        interactive: true
      }, async (response) => {
        if (chrome.runtime.lastError) {
          reject('Failed to launch the authentication flow')
        }

        // Should reject the promise automatically if anything goes wrong.
        const idToken = new URL(response).hash.match(/id_token=([^&]+)/)[1]
        const accessToken = new URL(response).hash.match(/access_token=([^&]+)/)[1]
        const profile = await axios.get('https://www.googleapis.com/oauth2/v3/userinfo', {
          headers: { Authorization: `Bearer ${accessToken}` }
        }).then(({ data }) => data)

        // Save the session data to the storage.
        return this._saveSession({
          idToken,
          accessToken,
          profile,
        }).then(resolve)
      })
    })
  },

  /**
   * Save the sesion data to the sync storage.
   *
   * @param {any} session The session data.
   */
  async _saveSession (session) {
    return new Promise((resolve) => {
      chrome.storage.sync.set({ 'auth.session': session }, () => resolve())
    })
  },

  /**
   * Destroy the session.
   */
  async _destroySession () {
    return new Promise((resolve) => {
      chrome.storage.sync.remove('auth.session', () => resolve())
    })
  },

  /**
   * Events are defined here.
   */
  onLogin: new Event(),

}
<|endoftext|>


<commit_before>// ==UserScript==
// @name        hvBazaarList
// @name:zh-CN  【HV】购物清单
// @namespace   https://github.com/dodying/Dodying-UserJs
// @description:zh-CN
// @include     http://hentaiverse.org/?s=Bazaar&ss=is*
// @version     1.00a
// @grant       unsafeWindow
// @author      Dodying
// @namespace   https://github.com/dodying/Dodying-UserJs
// @supportURL  https://github.com/dodying/Dodying-UserJs/issues
// @icon        https://raw.githubusercontent.com/dodying/UserJs/master/Logo.png
// @require     http://cdn.bootcss.com/jquery/2.1.4/jquery.min.js
// @run-at      document-end
// ==/UserScript==
(function ($) {
  $('

').html('ID:<input name="itemId"type="text">
Name:<input name="itemName"type="text">
Amount:<input name="itemAmount"type="text" placeholder="1000">
Round:<input name="itemFix"type="text" placeholder="100">
<button class="listHide">X</button> <button class="listDel">REMOVE</button> <button class="listSave">SAVE</button>').attr('style', 'position:absolute;top:120px;left:550px;z-index:9;background-color:#EDEBDF;border:1px solid black;display:none;').appendTo('body');
  $('.listHide').click(function () {
    $('.listBox').hide();
  });
  $('.listDel').click(function () {
    var info = getValue('BazaarList', true);
    if (!info) return;
    delete info[$('input[name=itemId]').val()];
    setValue('BazaarList', info);
    $('.listBox').hide();
  });
  $('.listSave').click(function () {
    var info = getValue('BazaarList', true) || new Object();
    info[$('input[name=itemId]').val()] = {
      name: $('input[name=itemName]').val(),
      amount: $('input[name=itemAmount]').val(),
      fix: $('input[name=itemFix]').val()
    };
    setValue('BazaarList', info);
    $('.listBox').hide();
  });
  $('').html('+').prependTo('.iop,.io').attr('style', 'cursor:pointer;font-size:large;').on('click', function () {
    var id = parseInt($(this).next().attr('id'));
    $('input[name=itemId]').val(id);
    if (getValue('BazaarList') && id in getValue('BazaarList', true)) {
      var info = getValue('BazaarList', true);
      $('input[name=itemName]').val(info[id].name);
      $('input[name=itemAmount]').val(info[id].amount);
      $('input[name=itemFix]').val(info[id].fix);
    } else {
      $('input[name=itemName]').val($('[id^=' + id + ']:eq(0)').text());
      $('input[name=itemAmount]').val('');
      $('input[name=itemFix]').val('');
    }
    $('.listBox').show();
  });
  $('<button></button>').text('Generate List').click(function () {
    var info = getValue('BazaarList', true);
    if (!info) return;
    var _html = '';
    var itemPane;
    var amount;
    var i;
    for (i in info) {
      itemPane = $('#' + i + 'item_pane');
      if (itemPane.length !== 0) {
        amount = parseInt(info[i].amount) - parseInt(itemPane.parent().next().text());
        if (amount < 0) continue;
        if (info[i].fix) amount = (Math.floor(amount / info[i].fix) + 1) * info[i].fix;
      } else {
        amount = info[i].amount;
      }
      _html += amount + 'x ' + info[i].name + '
';
    }
    var OpenWindow = unsafeWindow.open('', 'newwin', 'height=250,width=250,toolbar=no,menubar=no');
    OpenWindow.document.write(_html);
    OpenWindow.document.close();
  }).appendTo('.clb');
  function setValue(item, value) {
    localStorage[item] = (typeof value === 'string') ? value : JSON.stringify(value);
  }
  function getValue(item, toJSON) {
    return (localStorage[item]) ? ((toJSON) ? JSON.parse(localStorage[item])  : localStorage[item])  : null;
  }
}) ($);
<commit_msg>v1.00b<commit_after>// ==UserScript==
// @name        hvBazaarList
// @name:zh-CN  【HV】购物清单
// @namespace   https://github.com/dodying/Dodying-UserJs
// @description:zh-CN
// @include     http*://hentaiverse.org/?s=Bazaar&ss=is*
// @include     http*://alt.hentaiverse.org/?s=Bazaar&ss=is*
// @version     1.00b
// @grant       unsafeWindow
// @author      Dodying
// @namespace   https://github.com/dodying/Dodying-UserJs
// @supportURL  https://github.com/dodying/Dodying-UserJs/issues
// @icon        https://raw.githubusercontent.com/dodying/UserJs/master/Logo.png
// @require     http://cdn.bootcss.com/jquery/2.1.4/jquery.min.js
// @run-at      document-end
// ==/UserScript==
(function ($) {
  $('

').html('ID:<input name="itemId"type="text">
Name:<input name="itemName"type="text">
Amount:<input name="itemAmount"type="text" placeholder="1000">
Round:<input name="itemFix"type="text" placeholder="100">
<button class="listHide">X</button> <button class="listDel">REMOVE</button> <button class="listSave">SAVE</button>').attr('style', 'position:absolute;top:120px;left:550px;z-index:9;background-color:#EDEBDF;border:1px solid black;display:none;').appendTo('body');
  $('.listHide').click(function () {
    $('.listBox').hide();
  });
  $('.listDel').click(function () {
    var info = getValue('BazaarList', true);
    if (!info) return;
    delete info[$('input[name=itemId]').val()];
    setValue('BazaarList', info);
    $('.listBox').hide();
  });
  $('.listSave').click(function () {
    var info = getValue('BazaarList', true) || new Object();
    info[$('input[name=itemId]').val()] = {
      name: $('input[name=itemName]').val(),
      amount: $('input[name=itemAmount]').val(),
      fix: $('input[name=itemFix]').val()
    };
    setValue('BazaarList', info);
    $('.listBox').hide();
  });
  $('').html('+').prependTo('.iop,.io').attr('style', 'cursor:pointer;font-size:large;').on('click', function () {
    var id = parseInt($(this).next().attr('id'));
    $('input[name=itemId]').val(id);
    if (getValue('BazaarList') && id in getValue('BazaarList', true)) {
      var info = getValue('BazaarList', true);
      $('input[name=itemName]').val(info[id].name);
      $('input[name=itemAmount]').val(info[id].amount);
      $('input[name=itemFix]').val(info[id].fix);
    } else {
      $('input[name=itemName]').val($('[id^=' + id + ']:eq(0)').text());
      $('input[name=itemAmount]').val('');
      $('input[name=itemFix]').val('');
    }
    $('.listBox').show();
  });
  $('<button></button>').text('Generate List').click(function () {
    var info = getValue('BazaarList', true);
    if (!info) return;
    var _html = '';
    var itemPane;
    var amount;
    var i;
    for (i in info) {
      itemPane = $('#' + i + 'item_pane');
      if (itemPane.length !== 0) {
        amount = parseInt(info[i].amount) - parseInt(itemPane.parent().next().text());
        if (amount < 0) continue;
        if (info[i].fix) amount = (Math.floor(amount / info[i].fix) + 1) * info[i].fix;
      } else {
        amount = info[i].amount;
      }
      _html += amount + 'x ' + info[i].name + '
';
    }
    var OpenWindow = unsafeWindow.open('', 'newwin', 'height=250,width=250,toolbar=no,menubar=no');
    OpenWindow.document.write(_html);
    OpenWindow.document.close();
  }).appendTo('.clb');
  function setValue(item, value) {
    localStorage[item] = (typeof value === 'string') ? value : JSON.stringify(value);
  }
  function getValue(item, toJSON) {
    return (localStorage[item]) ? ((toJSON) ? JSON.parse(localStorage[item])  : localStorage[item])  : null;
  }
}) ($);
<|endoftext|>


<commit_before>const t9e = function() {};

/**
 * Generate a square multidimensional array of arbitrary size.
 *
 * @param {number} [size=3] - Size of grid (x and y).
 * @param {function=} callback - Optional callback to prepopulate fields.
 * @return {array} array - Square multidimensional array.
 */

t9e.prototype.grid = function(size, callback) {
    if (size === undefined) {
        size = 3;
    }

    let array = [...Array(size).keys()];

    if (this.isFunction(callback)) {
        array = array.map(callback);
    }

    return array.map(() => array);
};

/**
 * Test whether a variable is a function.
 *
 * @param {object} callback - Object to test.
 * @return {bool} - Object is a function, true/false.
 */

t9e.prototype.isFunction = function(callback) {
    return callback && Object.prototype.toString.call(callback) === '[object Function]';
};

/**
 * Transpose an array.
 *
 * This is used to assist in evaluation of win conditions: Rather than change the
 * test to fit board state, it's easier to fit the board to a single win
 * condition (by turning the board sideways).
 *
 *  1. If array is empty or falsy, return an array.
 *  2. If array[0] is not an array itself, return the parent reversed.
 *
 * @param {array} array - Multidimensional array of board cells.
 * @return {array} array - Original array transposed.
 */

t9e.prototype.transpose = function(array) {
    if (!array || !array.length) {
        return [];
    }

    if (!Array.isArray(array[0])) {
        return array.slice().reverse();
    }

    return array[0].map((_, index) => array.map(row => row[index]));
};

/**
 * Return the diagonal elements of a square multidimensional array.
 *
 * @param {array} array - Multidimensional array of board cells.
 * @param {array} array - Multidimensional array of diagonal elements.
 */

t9e.prototype.diagonals = function(array) {
    const reversed = array.slice().reverse();

    return [
        array.map((element, index) => array[index][index]),
        array.map((element, index) => reversed[index][index])
    ];
};

/**
 * Test whether a board has _any_ empty cells.
 *
 * '', null and undefined are falsy in JavaScript, and thus coerce to false.
 * The inner loops sees whether any cell contains a falsy value.
 *
 * @param {array} board - Multidimensional array of board cells.
 * @return {bool} - At least one cell of the board is empty, true/false.
 */

t9e.prototype.anyEmpty = function(board) {
    return board.some(row => row.some(cell => !cell));
};

/**
 * Test whether an array is a sqare multidimensional array.
 *
 * @param {array} array
 * @return {bool} - Array is multidimensonal and square, true/false.
 */

t9e.prototype.isSquare = function(array) {
    const size = array.length;

    return array.every(element => {
        return Array.isArray(element) && element.length === size;
    });
};

/**
 * Test whether a row of the passed game board contains the supplied character,
 * while the board is in the following configurations:
 *
 *  1. Board rows.
 *  2. Board columns (transposed to rows).
 *  3. Board diagonals (converted to rows).
 *
 * To explain: Rather than changing the test pattern to fit every board
 * configuration, it is easier to change the configuration to fit a single test
 * pattern:
 *
 * Does any row of the board contain _only_ the supplied character?
 *
 * @param {string} character - Character to test.
 * @param {array} board - Multidimensional array to test.
 * @param {bool} - A row of the board all contain the given character, true/false.
 */

t9e.prototype.check = function(character, board) {
    if (!Array.isArray(board) || !this.isSquare(board)) {
        return false;
    }

    return [].concat(board, this.transpose(board), this.diagonals(board)).some(row => {
        return row.every(cell => cell === character);
    });
};

module.exports = Object.create(t9e.prototype);
<commit_msg>Further tweak.<commit_after>const t9e = function() {};

/**
 * Generate a square multidimensional array of arbitrary size.
 *
 * @param {number} [size=3] - Size of grid (x and y).
 * @param {function=} callback - Optional callback to prepopulate fields.
 * @return {array} array - Square multidimensional array.
 */

t9e.prototype.grid = function(size, callback) {
    if (size === undefined) {
        size = 3;
    }

    let array = [...Array(size).keys()];

    if (this.isFunction(callback)) {
        array = array.map(callback);
    }

    return array.map(() => array);
};

/**
 * Test whether a variable is a function.
 *
 * @param {object} callback - Object to test.
 * @return {bool} - Object is a function, true/false.
 */

t9e.prototype.isFunction = function(callback) {
    return callback && Object.prototype.toString.call(callback) === '[object Function]';
};

/**
 * Transpose an array.
 *
 * This is used to assist in evaluation of win conditions: Rather than change the
 * test to fit board state, it's easier to fit the board to a single win
 * condition (by turning the board sideways).
 *
 *  1. If array is empty or falsy, return an array.
 *  2. If array[0] is not an array itself, return the parent reversed.
 *
 * @param {array} array - Multidimensional array of board cells.
 * @return {array} array - Original array transposed.
 */

t9e.prototype.transpose = function(array) {
    if (!array || !array.length) {
        return [];
    }

    if (!Array.isArray(array[0])) {
        return array.slice().reverse();
    }

    return array[0].map((_, index) => array.map(row => row[index]));
};

/**
 * Return the diagonal elements of a square multidimensional array.
 *
 * @param {array} array - Multidimensional array of board cells.
 * @param {array} array - Multidimensional array of diagonal elements.
 */

t9e.prototype.diagonals = function(array) {
    const reversed = array.slice().reverse();

    return [
        array.map((element, index) => array[index][index]),
        array.map((element, index) => reversed[index][index])
    ];
};

/**
 * Test whether a board has _any_ empty cells.
 *
 * '', null and undefined are falsy in JavaScript, and thus coerce to false.
 * The inner loops sees whether any cell contains a falsy value.
 *
 * @param {array} board - Multidimensional array of board cells.
 * @return {bool} - At least one cell of the board is empty, true/false.
 */

t9e.prototype.anyEmpty = function(board) {
    return board.some(row => row.some(cell => !cell));
};

/**
 * Test whether an array is a sqare multidimensional array.
 *
 * @param {array} array
 * @return {bool} - Array is multidimensonal and square, true/false.
 */

t9e.prototype.isSquare = function(array) {
    const size = array.length;

    return array.every(element => {
        return Array.isArray(element) && element.length === size;
    });
};

/**
 * Test whether a row of the passed game board contains the supplied character,
 * while the board is in the following configurations:
 *
 *  1. Board rows.
 *  2. Board columns (transposed to rows).
 *  3. Board diagonals (converted to rows).
 *
 * To explain: Rather than changing the test pattern to fit every board
 * configuration, it is easier to change the configuration to fit a single test
 * pattern:
 *
 * Does any row of the board contain _only_ the supplied character?
 *
 * @param {string} character - Character to test.
 * @param {array} board - Multidimensional array to test.
 * @param {bool} - A row of the board all contain the given character, true/false.
 */

t9e.prototype.check = function(character, board) {
    if (!board || !Array.isArray(board) || !this.isSquare(board)) {
        return false;
    }

    return [].concat(board, this.transpose(board), this.diagonals(board)).some(row => {
        return row.every(cell => cell === character);
    });
};

module.exports = Object.create(t9e.prototype);
<|endoftext|>


<commit_before>(function() {
    // Because bug 673873 kills me.
    $('input[placeholder]').on('focus', function() {
        var $this = $(this);
        $this.data('placeholder', $this.attr('placeholder'))
             .removeAttr('placeholder');
    }).on('blur', function() {
        var $this = $(this);
        $this.attr('placeholder', $this.data('placeholder'));
    });

    // Add 'sel' class to active filter and set hidden input value.
    z.page.on('click', '#filters .toggles a', function() {
        selectMe($(this));
        return false;
    }).on('click', '#filters .confirm-action a', function() {
        var $this = $(this);
        selectMe($this);
        $this.closest('.confirm-action').removeClass('show');
        $('.picker').toggleClass('sel');
        return false;
    });

    function selectMe($elm) {
        var $myUL = $elm.closest('ul'),
            val = '',
            vars = z.getVars($elm[0].search);

        if ($elm.hasClass('cancel')) {
            return;
        }
        $myUL.find('a').removeClass('sel');

        if ($myUL[0].id == 'filter-prices') {
            val = vars.price || '';
        } else if ($myUL[0].id == 'filter-categories') {
            val = vars.cat || '';
            $('.picker').text($elm.text());
        } else if ($myUL[0].id == 'filter-sort') {
            val = vars.sort || '';
        }
        $myUL.find('+ input[type=hidden]').val(val);
        $elm.addClass('sel');
    }

    // Apply filters button.
    z.page.on('click', '#filters .apply', _pd(function() {
        $('#filters form').submit();
    })).on('click', '#filters .picker', _pd(function() {
        $('#filter-categories').addClass('show');
        if ($('#filter-categories li').length > z.confirmBreakNum) {
            $('#filter-categories').addClass('two-col');
        } else {
            $('#filter-categories').removeClass('two-col');
        }
        $(this).toggleClass('sel');
    })).on('click', '.confirm-action .cancel', _pd(function(e) {
        $(e.target).closest('.confirm-action').removeClass('show');
    }));

    var expandListings = false;
    var $expandToggle = $('#site-header .expand');

    // Toggle app listing graphical/compact view.
    $expandToggle.click(_pd(function(e) {
        expandListings = !expandListings;
        setTrays(expandListings);
    }));

    z.page.on('fragmentloaded', function() {
        if (z.body.data('page-type') === 'search') {
            expandListings = localStorage.getItem('expand-listings') === 'true';
            if (expandListings) {
                setTrays(true);
            }
        }
    });

    function setTrays(expanded) {
        $('ol.listing').toggleClass('expanded', expanded);
        $expandToggle.toggleClass('active', expanded);
        localStorage.setItem('expand-listings', expanded);
        if (expanded) {
            z.page.trigger('populatetray');
        }
    }

    function turnPages(e) {
        if (fieldFocused(e)) {
            return;
        }
        if (e.which == z.keys.LEFT || e.which == z.keys.RIGHT) {
            var sel;
            if (e.which == z.keys.LEFT) {
                sel = '.paginator .prev:not(.disabled)';
            } else {
                sel = '.paginator .next:not(.disabled)';
            }
            var href = $(sel).attr('href');
            if (href) {
                e.preventDefault();
                $(window).trigger('loadfragment', href);
            }
        }
    }
    $(document).keyup(_.throttle(turnPages, 300));
})();
<commit_msg>Fix expand switch (bug 792150)<commit_after>(function() {
    // Because bug 673873 kills me.
    $('input[placeholder]').on('focus', function() {
        var $this = $(this);
        $this.data('placeholder', $this.attr('placeholder'))
             .removeAttr('placeholder');
    }).on('blur', function() {
        var $this = $(this);
        $this.attr('placeholder', $this.data('placeholder'));
    });

    // Add 'sel' class to active filter and set hidden input value.
    z.page.on('click', '#filters .toggles a', function() {
        selectMe($(this));
        return false;
    }).on('click', '#filters .confirm-action a', function() {
        var $this = $(this);
        selectMe($this);
        $this.closest('.confirm-action').removeClass('show');
        $('.picker').toggleClass('sel');
        return false;
    });

    function selectMe($elm) {
        var $myUL = $elm.closest('ul'),
            val = '',
            vars = z.getVars($elm[0].search);

        if ($elm.hasClass('cancel')) {
            return;
        }
        $myUL.find('a').removeClass('sel');

        if ($myUL[0].id == 'filter-prices') {
            val = vars.price || '';
        } else if ($myUL[0].id == 'filter-categories') {
            val = vars.cat || '';
            $('.picker').text($elm.text());
        } else if ($myUL[0].id == 'filter-sort') {
            val = vars.sort || '';
        }
        $myUL.find('+ input[type=hidden]').val(val);
        $elm.addClass('sel');
    }

    // Apply filters button.
    z.page.on('click', '#filters .apply', _pd(function() {
        $('#filters form').submit();
    })).on('click', '#filters .picker', _pd(function() {
        $('#filter-categories').addClass('show');
        if ($('#filter-categories li').length > z.confirmBreakNum) {
            $('#filter-categories').addClass('two-col');
        } else {
            $('#filter-categories').removeClass('two-col');
        }
        $(this).toggleClass('sel');
    })).on('click', '.confirm-action .cancel', _pd(function(e) {
        $(e.target).closest('.confirm-action').removeClass('show');
    }));

    var expandListings = localStorage.getItem('expand-listings') === 'true';
    var $expandToggle = $('#site-header .expand');

    // Toggle app listing graphical/compact view.
    $expandToggle.click(_pd(function(e) {
        expandListings = !expandListings;
        setTrays(expandListings);
    }));

    z.page.on('fragmentloaded', function() {
        if (z.body.data('page-type') === 'search') {
            setTrays(expandListings);
        }
    });

    function setTrays(expanded) {
        $('ol.listing').toggleClass('expanded', expanded);
        $expandToggle.toggleClass('active', expanded);
        localStorage.setItem('expand-listings', expanded);
        if (expanded) {
            z.page.trigger('populatetray');
        }
    }

    function turnPages(e) {
        if (fieldFocused(e)) {
            return;
        }
        if (e.which == z.keys.LEFT || e.which == z.keys.RIGHT) {
            var sel;
            if (e.which == z.keys.LEFT) {
                sel = '.paginator .prev:not(.disabled)';
            } else {
                sel = '.paginator .next:not(.disabled)';
            }
            var href = $(sel).attr('href');
            if (href) {
                e.preventDefault();
                $(window).trigger('loadfragment', href);
            }
        }
    }
    $(document).keyup(_.throttle(turnPages, 300));
})();
<|endoftext|>


<commit_before>/**
 * Form validation
 */
bolt.validation = (function () {

    /**
     * Basic legacy validation checking
     * Adapted from  http://www.sitepoint.com/html5-forms-javascript-constraint-validation-api/
     *
     * @param {Object} field - Field element
     * @returns {boolean}
     */
    function legacyValidation(field) {
        var
            valid = true,
            val = field.value,
            type = field.getAttribute('type'),
            chkbox = type === 'checkbox' || type === 'radio',
            required = field.getAttribute('required'),
            minlength = field.getAttribute('minlength'),
            maxlength = field.getAttribute('maxlength'),
            pattern = field.getAttribute('pattern');

        // Disabled fields should not be validated
        if (field.disabled) {
            return valid;
        }

        /* jshint -W126 */

        // value required?
        valid = valid && (!required ||
            (chkbox && field.checked) ||
            (!chkbox && val !== "")
        );

        // minlength or maxlength set?
        valid = valid && (chkbox || (
            (!minlength || val.length >= minlength) &&
            (!maxlength || val.length <= maxlength)
        ));

        /* jshint +W126 */

        // Test pattern
        if (valid && pattern) {
            pattern = new RegExp('^(?:' + pattern + ')$');
            valid = pattern.test(val);
        }

        return valid;
    }

    /**
     * Adds error classes to field
     *
     * @param {Object} field - Field element
     * @param {boolean} isCkeditor - Is a isCkeditor field
     */
    function addErrorClass(field, isCkeditor) {
        $(field).addClass('error');

        if (isCkeditor) {
            $('#cke_' + field.id).addClass('cke_error');
        }
    }

    /**
     * Removes error classes from field
     *
     * @param {Object} field - Field element
     * @param {boolean} isCkeditor - Is a isCkeditor field
     */
    function removeErrorClass(field, isCkeditor) {
        $(field).removeClass('error');

        if (isCkeditor) {
            $('#cke_' + field.id).removeClass('cke_error');
        }
    }

    /**
     * Show alertbox
     *
     * @param {string} id - Id of the alertbox
     * @param {string} msg - Message text
     */
    function showAlertbox(id, msg) {
        var alertbox = bolt.data.editcontent.error.alertbox.subst({
            '%NOTICE_ID%': id,
            '%MESSAGE%': msg
        });

        $(alertbox)
            .hide()
            .insertAfter('.page-header')
            .slideDown('fast');
    }

    /**
     * Validates a field
     *
     * @param {Object} field - Field element
     * @returns {boolean}
     */
    function validate(field) {
        var hasNativeValidation,
            isCkeditor;

        // Is native browser validation available?
        hasNativeValidation = typeof field.willValidate !== 'undefined';
        if (hasNativeValidation) {
            // Native validation available
            if (field.nodeName === 'INPUT' && field.type !== field.getAttribute('type')) {
                // Input type not supported! Use legacy JavaScript validation
                field.setCustomValidity(legacyValidation(field) ? '' : 'error');
            }
            // Native browser check
            field.checkValidity();
        } else {
            // Native validation not available
            field.validity = field.validity || {};
            // Set to result of validation function
            field.validity.valid = legacyValidation(field);

            // If "invalid" events are required, trigger it here
        }

        // Special validation for CKEditor fields
        isCkeditor = field.nodeName === 'TEXTAREA' && $(field).hasClass('ckeditor');
        if (isCkeditor) {
            var editor = CKEDITOR.instances[field.id],
                error;

            if (editor) {
                error = editor._.required === true && editor.getData().trim() === '';
                if (hasNativeValidation) {
                    field.setCustomValidity(error ? 'Required' : '');
                } else {
                    field.validity.valid = error;
                }
            }
        }

        var noticeID = 'notice--' + field.id;

        // First, remove any existing old notices
        $('#' + noticeID).remove();

        if (field.validity.valid) {
            removeErrorClass(field, isCkeditor);

            return true;
        } else {
            addErrorClass(field, isCkeditor);

            var msg = $(field).data('errortext');
            if (!msg) {
                msg = bolt.data.editcontent.error.msg.subst({'%FIELDNAME%': field.name});
            }
            showAlertbox(noticeID, msg);

            return false;
        }
    }

    return {
        /**
         * Validates all inputs of a form
         *
         * @param {Object} form - Form element
         * @returns {boolean}
         */
        run: function (form) {
            var formLength = form.elements.length,
                f,
                field,
                formvalid = true;

            // Loop all fields
            for (f = 0; f < formLength; f++) {
                field = form.elements[f];

                if (field.nodeName !== 'INPUT' && field.nodeName !== 'TEXTAREA' && field.nodeName !== 'SELECT') {
                    continue;
                }

                if (field.nodeName === 'INPUT') {
                    // Trim input values
                    field.value = field.value.trim();
                }

                if (validate(field) === false) {
                    formvalid = false;
                }
            }

            return formvalid;
        }
    };
} ());
<commit_msg>Add basic sceleton for validation<commit_after>/**
 * Form validation
 */
bolt.validation = (function () {

    /**
     * Basic legacy validation checking
     * Adapted from  http://www.sitepoint.com/html5-forms-javascript-constraint-validation-api/
     *
     * @param {Object} field - Field element
     * @returns {boolean}
     */
    function legacyValidation(field) {
        var
            valid = true,
            val = field.value,
            type = field.getAttribute('type'),
            chkbox = type === 'checkbox' || type === 'radio',
            required = field.getAttribute('required'),
            minlength = field.getAttribute('minlength'),
            maxlength = field.getAttribute('maxlength'),
            pattern = field.getAttribute('pattern');

        // Disabled fields should not be validated
        if (field.disabled) {
            return valid;
        }

        /* jshint -W126 */

        // value required?
        valid = valid && (!required ||
            (chkbox && field.checked) ||
            (!chkbox && val !== "")
        );

        // minlength or maxlength set?
        valid = valid && (chkbox || (
            (!minlength || val.length >= minlength) &&
            (!maxlength || val.length <= maxlength)
        ));

        /* jshint +W126 */

        // Test pattern
        if (valid && pattern) {
            pattern = new RegExp('^(?:' + pattern + ')$');
            valid = pattern.test(val);
        }

        return valid;
    }

    /**
     * Adds error classes to field
     *
     * @param {Object} field - Field element
     * @param {boolean} isCkeditor - Is a isCkeditor field
     */
    function addErrorClass(field, isCkeditor) {
        $(field).addClass('error');

        if (isCkeditor) {
            $('#cke_' + field.id).addClass('cke_error');
        }
    }

    /**
     * Removes error classes from field
     *
     * @param {Object} field - Field element
     * @param {boolean} isCkeditor - Is a isCkeditor field
     */
    function removeErrorClass(field, isCkeditor) {
        $(field).removeClass('error');

        if (isCkeditor) {
            $('#cke_' + field.id).removeClass('cke_error');
        }
    }

    /**
     * Show alertbox
     *
     * @param {string} id - Id of the alertbox
     * @param {string} msg - Message text
     */
    function showAlertbox(id, msg) {
        var alertbox = bolt.data.editcontent.error.alertbox.subst({
            '%NOTICE_ID%': id,
            '%MESSAGE%': msg
        });

        $(alertbox)
            .hide()
            .insertAfter('.page-header')
            .slideDown('fast');
    }

    /**
     * Validates a field
     *
     * @param {Object} field - Field element
     * @returns {boolean}
     */
    function validate(field) {
        var hasNativeValidation,
            isCkeditor,
            task,
            value;

        var validates = $(field).data('validate');
        if (validates) {
            for (task in validates) {
                value = validates[task];

                switch (task) {
                    case 'float':
                        break;

                    case 'required':
                        break;

                    case 'min':
                        break;

                    case 'max':
                        break;

                    default:
                        console.log('UNKNOWN VALIDATION' + task + " -> " + value);
                }
            }
        } else {
            // Is native browser validation available?
            hasNativeValidation = typeof field.willValidate !== 'undefined';
            if (hasNativeValidation) {
                // Native validation available
                if (field.nodeName === 'INPUT' && field.type !== field.getAttribute('type')) {
                    // Input type not supported! Use legacy JavaScript validation
                    field.setCustomValidity(legacyValidation(field) ? '' : 'error');
                }
                // Native browser check
                field.checkValidity();
            } else {
                // Native validation not available
                field.validity = field.validity || {};
                // Set to result of validation function
                field.validity.valid = legacyValidation(field);

                // If "invalid" events are required, trigger it here
            }

            // Special validation for CKEditor fields
            isCkeditor = field.nodeName === 'TEXTAREA' && $(field).hasClass('ckeditor');
            if (isCkeditor) {
                var editor = CKEDITOR.instances[field.id],
                    error;

                if (editor) {
                    error = editor._.required === true && editor.getData().trim() === '';
                    if (hasNativeValidation) {
                        field.setCustomValidity(error ? 'Required' : '');
                    } else {
                        field.validity.valid = error;
                    }
                }
            }
        }

        var noticeID = 'notice--' + field.id;

        // First, remove any existing old notices
        $('#' + noticeID).remove();

        if (field.validity.valid) {
            removeErrorClass(field, isCkeditor);

            return true;
        } else {
            addErrorClass(field, isCkeditor);

            var msg = $(field).data('errortext');
            if (!msg) {
                msg = bolt.data.editcontent.error.msg.subst({'%FIELDNAME%': field.name});
            }
            showAlertbox(noticeID, msg);

            return false;
        }
    }

    return {
        /**
         * Validates all inputs of a form
         *
         * @param {Object} form - Form element
         * @returns {boolean}
         */
        run: function (form) {
            var formLength = form.elements.length,
                f,
                field,
                formvalid = true;

            // Loop all fields
            for (f = 0; f < formLength; f++) {
                field = form.elements[f];

                if (field.nodeName !== 'INPUT' && field.nodeName !== 'TEXTAREA' && field.nodeName !== 'SELECT') {
                    continue;
                }

                if (field.nodeName === 'INPUT') {
                    // Trim input values
                    field.value = field.value.trim();
                }

                if (validate(field) === false) {
                    formvalid = false;
                }
            }

            return formvalid;
        }
    };
} ());
<|endoftext|>


<commit_before>/**
 * @class thorough
 * @description 深入学习 js
 * @time 2015-02-25 11:08
 * @author StarZou
 **/
(function (window, document) {
    'use strict';

    var tilteElement = document.getElementById('tilte');
    var name = '深入学习 js';

    tilteElement.textContent = name;

})(window, document);<commit_msg>深入学习 js<commit_after>/**
 * @class thorough
 * @description 深入学习 js
 * @time 2015-02-25 11:08
 * @author StarZou
 **/
(function (window, document) {
    'use strict';

    var tilteElement = document.getElementById('tilte');
    var name = '深入学习 js';

    tilteElement.textContent = name;

    function f() {
        var name = 'f 函数 中的name';

        console.log('f this', this);

        function getName() {
            return name;
        }

        return getName;
    }

    var getName = f.call({name: 'f warp'});
    var myName = getName();

    console.log(myName);

})(window, document);<|endoftext|>


<commit_before>/* like button */

// keep "like" button on the same line as "share on"
var previousDistance_dsShare_dsThread = "0px", class_dsMeta_previousWidth = 0;
var dsElementsReady = false;
var class_pseudoDsMeta, class_dsShare, class_dsThread, class_dsMeta;
setInterval(function () {
  // make sure elements needed have been properly gotten
  if (!dsElementsReady) {
    class_pseudoDsMeta = document.getElementsByClassName("pseudo-ds-meta");
    class_dsShare = document.getElementsByClassName("ds-share"),
    class_dsThread = document.getElementsByClassName("ds-thread");
    class_dsMeta = document.getElementsByClassName("ds-meta");
    if (typeof(class_dsMeta[0]) == "undefined" || typeof(class_pseudoDsMeta[0]) == "undefined") return;
    else dsElementsReady = true;
    modifyShareOn();
  }

  // raise "like" button
  var currentDistance_dsShare_dsThread = class_dsShare[0].offsetTop - class_dsThread[0].offsetTop - 5 + "px";
  if (previousDistance_dsShare_dsThread != currentDistance_dsShare_dsThread) {
    previousDistance_dsShare_dsThread = class_dsMeta[0].style.top = currentDistance_dsShare_dsThread;
  }

  // change the offset of "share on" as the width of "like" button changes
  var class_dsMeta_currentWidth = class_dsMeta[0].clientWidth;
  if (class_dsMeta_previousWidth != class_dsMeta_currentWidth) {
    class_pseudoDsMeta[0].style.width = (class_dsMeta_currentWidth > 0) ? (class_dsMeta_currentWidth + 20 + "px") : "0";
    class_dsMeta_previousWidth = class_dsMeta_currentWidth;
  }

}, 60);

/* share on */

function modifyShareOn() {
  // make sure to get the "share on" element
  var class_dsShareIcons = undefined;
  while (typeof(class_dsShareIcons) == "undefined") class_dsShareIcons = document.getElementsByClassName("ds-share-icons-16");

  // fix "undefined" problem
  document.getElementsByClassName("ds-douban")[0].innerHTML = "Douban";
  document.getElementsByClassName("ds-youdao")[0].innerHTML = "Youdao CloudNote";
  // document.getElementsByClassName("ds-more")[0].innerHTML = "Share on";
}

/* back to top button */

var backToTop = document.getElementById("backtotop");
var addEventListenerExistence = window.addEventListener;
if (typeof(addEventListenerExistence) != "undefined") {
  backToTop.addEventListener('click', function () {
    var documentBody = document.body;
    var distanceFromTop = documentBody.scrollTop;
    var pace = (distanceFromTop > 1250) ? 100 : distanceFromTop / 12.5;
    window.requestAnimationFrame((function () {
      var calculatedScrollTop = parseInt(documentBody.scrollTop) - pace;
      documentBody.scrollTop = ((calculatedScrollTop < 0) ? 0 : calculatedScrollTop);
      if (parseInt(documentBody.scrollTop) > 0) window.requestAnimationFrame(arguments.callee);
    }));
  }, false);
}
else {
  backToTop.attachEvent('onclick', function () {
    var documentBody = document.body;
    var pace = documentBody.scrollTop / 12.5;

  });
}

/* gif view again */
var viewAgains = document.getElementsByClassName("view-again");
for (var i = 0; i < viewAgains.length; i++) {
  var viewAgain = viewAgains[i];
  viewAgain.addEventListener('click', function() {
    var img = this.parentNode.nextElementSibling.firstChild;
    var imgSrc = img.src;
    img.src = "https://www.baidu.com/img/bd_logo1.png";
    img.src = imgSrc;
  }, false);
}<commit_msg>adjust backtotop button's speed<commit_after>/* like button */

// keep "like" button on the same line as "share on"
var previousDistance_dsShare_dsThread = "0px", class_dsMeta_previousWidth = 0;
var dsElementsReady = false;
var class_pseudoDsMeta, class_dsShare, class_dsThread, class_dsMeta;
setInterval(function () {
  // make sure elements needed have been properly gotten
  if (!dsElementsReady) {
    class_pseudoDsMeta = document.getElementsByClassName("pseudo-ds-meta");
    class_dsShare = document.getElementsByClassName("ds-share"),
    class_dsThread = document.getElementsByClassName("ds-thread");
    class_dsMeta = document.getElementsByClassName("ds-meta");
    if (typeof(class_dsMeta[0]) == "undefined" || typeof(class_pseudoDsMeta[0]) == "undefined") return;
    else dsElementsReady = true;
    modifyShareOn();
  }

  // raise "like" button
  var currentDistance_dsShare_dsThread = class_dsShare[0].offsetTop - class_dsThread[0].offsetTop - 5 + "px";
  if (previousDistance_dsShare_dsThread != currentDistance_dsShare_dsThread) {
    previousDistance_dsShare_dsThread = class_dsMeta[0].style.top = currentDistance_dsShare_dsThread;
  }

  // change the offset of "share on" as the width of "like" button changes
  var class_dsMeta_currentWidth = class_dsMeta[0].clientWidth;
  if (class_dsMeta_previousWidth != class_dsMeta_currentWidth) {
    class_pseudoDsMeta[0].style.width = (class_dsMeta_currentWidth > 0) ? (class_dsMeta_currentWidth + 20 + "px") : "0";
    class_dsMeta_previousWidth = class_dsMeta_currentWidth;
  }

}, 60);

/* share on */

function modifyShareOn() {
  // make sure to get the "share on" element
  var class_dsShareIcons = undefined;
  while (typeof(class_dsShareIcons) == "undefined") class_dsShareIcons = document.getElementsByClassName("ds-share-icons-16");

  // fix "undefined" problem
  document.getElementsByClassName("ds-douban")[0].innerHTML = "Douban";
  document.getElementsByClassName("ds-youdao")[0].innerHTML = "Youdao CloudNote";
  // document.getElementsByClassName("ds-more")[0].innerHTML = "Share on";
}

/* back to top button */

var backToTop = document.getElementById("backtotop");
var addEventListenerExistence = window.addEventListener;
if (typeof(addEventListenerExistence) != "undefined") {
  backToTop.addEventListener('click', function () {
    var documentBody = document.body;
    var distanceFromTop = documentBody.scrollTop;
    var pace = (distanceFromTop > 1875) ? 150 : distanceFromTop / 12.5;
    window.requestAnimationFrame((function () {
      var calculatedScrollTop = parseInt(documentBody.scrollTop) - pace;
      documentBody.scrollTop = ((calculatedScrollTop < 0) ? 0 : calculatedScrollTop);
      if (parseInt(documentBody.scrollTop) > 0) window.requestAnimationFrame(arguments.callee);
    }));
  }, false);
}
else {
  backToTop.attachEvent('onclick', function () {
    var documentBody = document.body;
    var pace = documentBody.scrollTop / 12.5;

  });
}

/* gif view again */
var viewAgains = document.getElementsByClassName("view-again");
for (var i = 0; i < viewAgains.length; i++) {
  var viewAgain = viewAgains[i];
  viewAgain.addEventListener('click', function() {
    var img = this.parentNode.nextElementSibling.firstChild;
    var imgSrc = img.src;
    img.src = "https://www.baidu.com/img/bd_logo1.png";
    img.src = imgSrc;
  }, false);
}<|endoftext|>


<commit_before>'use strict';

angular.module('lmApp')
.controller('PostCtrl', function($scope, $http, $log, ngDialog, uiGmapGoogleMapApi) {
  $scope.place = {};

  uiGmapGoogleMapApi.then(function(maps) {
    $scope.googleVersion = maps.version;
    maps.visualRefresh = true;
  });

  angular.extend($scope, {
    map: {
      center: {
        latitude: 45,
        longitude: -73
      },
      zoom: 14,
      dragging: false,
      bounds: {},
      clickedMarker: {
        id: 0,
        options: {},
        title: 'You clicked here',
        latitude: null,
        longitude: null
      },
      events: {
        click: function (mapModel, eventName, originalEventArgs) {
          // 'this' is the directive's scope
          var e = originalEventArgs[0],
          lat = e.latLng.lat(),
          lon = e.latLng.lng();
          // Call the ngDialog from here show address
          // only house number should be filled in by user
          ngDialog.open({
            template: 'postDetails',
            scope: $scope
          });
          $scope.$apply( function () {
            $scope.map.clickedMarker = {
              id: 0,
              options: {
                labelContent: 'You clicked here ' + 'lat: ' + lat + ' lon: ' + lon,
                labelClass: "marker-labels",
                labelAnchor:"50 0"
              },
              latitude: lat,
              longitude: lon
            };
          });
        }
      }
    }
  });

  $scope.addPost = function () {
    $http.post('/api/rooms', {
      houseId: $scope.place.number,
      availability: false,
      address: $scope.place.addressLine1,
      area: $scope.place.addressLine2,
      geolocation: {
        type: "Point",
        coordinates: [12,13]
      }
    }).
      success(function (data) {
      $log.log("Success");
    }).
      error(function () {
      $log.log("Failure");
    });
  }

  var changePlaceCB =  function (searchBox) {
    var places = searchBox.getPlaces();
    if (places.length === 0) {
      return;
    }
    //change map to selected location
    $scope.map.center.latitude = places[0].geometry.location.lat();
    $scope.map.center.longitude = places[0].geometry.location.lng();
  }

  $scope.searchbox = {
    //template to load for the search box !mandatory field
    template: 'app/search/search.html',
    //position of search bar
    position: 'top-right',
    // Only one event associated with search box
    // See @https://developers.google.com/maps/documentation/javascript/reference#SearchBoxOptions
    events: {
      // format event: callback
      places_changed: changePlaceCB
    }
  };

});
<commit_msg>Using rootScope  accessing the callback set...<commit_after>'use strict';

angular.module('lmApp')
.controller('PostCtrl', Controller);

Controller.$inject = ['$scope', '$http', '$log', 'ngDialog', 'uiGmapGoogleMapApi', '$rootScope'];

function Controller ($scope, $http, $log, ngDialog, uiGmapGoogleMapApi, $rootScope) {
  $scope.place = {};

  uiGmapGoogleMapApi.then(function(maps) {
    $scope.googleVersion = maps.version;
    maps.visualRefresh = true;
  });

  angular.extend($scope, {
    map: {
      center: {
        latitude: 45,
        longitude: -73
      },
      zoom: 14,
      dragging: false,
      bounds: {},
      clickedMarker: {
        id: 0,
        options: {},
        title: 'You clicked here',
        latitude: null,
        longitude: null
      },
      events: {
        click: function (mapModel, eventName, originalEventArgs) {
          // 'this' is the directive's scope
          var e = originalEventArgs[0];
          $rootScope.latitude = e.latLng.lat();
          $rootScope.longitude = e.latLng.lng();
          // Call the ngDialog from here show address
          // only house number should be filled in by user
          $rootScope.dialogBox = ngDialog.open({
            template: 'postDetails',
            scope: $scope
          });
          $rootScope.$apply();
        }
      }
    }
  });

  $scope.addPost = function () {

    $http.post('/api/rooms', {
      houseId: $scope.place.number,
      availability: false,
      address: $scope.place.addressLine1,
      area: $scope.place.addressLine2,
      geolocation: {
        type: "Point",
        coordinates: [$rootScope.latitude,$rootScope.longitude]
      }
    }).success(function (data) {
      $rootScope.dialogBox.close();
      $log.log("Success");
    }).error(function () {
      $rootScope.dialogBox.close();
      $log.log("Failure");
    });
  }

  var changePlaceCB =  function (searchBox) {
    var places = searchBox.getPlaces();
    if (places.length === 0) {
      return;
    }
    //change map to selected location
    $scope.map.center.latitude = places[0].geometry.location.lat();
    $scope.map.center.longitude = places[0].geometry.location.lng();
  }

  $scope.searchbox = {
    //template to load for the search box !mandatory field
    template: 'app/search/search.html',
    //position of search bar
    position: 'top-right',
    // Only one event associated with search box
    // See @https://developers.google.com/maps/documentation/javascript/reference#SearchBoxOptions
    events: {
      // format event: callback
      places_changed: changePlaceCB
    }
  };
};
<|endoftext|>


<commit_before>/*

REM: Remedial Rest Interfaces

A library that simplifies and normalizes access to REST APIs.

Reference:
http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven

*/

// Namespace.
var rem = typeof exports == 'undefined' ? this.rem = {} : exports;
var remutil = typeof require == 'undefined' ? remutil : require('./remutil');

// Configuration.
rem.USER_AGENT = 'Mozilla/5.0 (compatible; REMbot/1.0; +http://rem.tcr.io/)';
rem.CONFIG_FILE = null;

/**
 * A hypermedia resource.
 */

var HyperMedia = (function () {

  function HyperMedia (api, res, data) {
    this.api = api;
    this.res = res;
    this.data = data;

    this.type = api.format;
    this.statusCode = Number(this.res.statusCode);
    this.err = this.statusCode > 400 ? this.statusCode : 0;

    // Parse body
    try {
      if (this.type === 'xml') {
        this.data = this.xml = rem.parsers.xml(String(this.data));
      } else {
        this.data = this.json = JSON.parse(String(this.data));
      }
    } catch (e) {
      this.err = e;
      console.warn('Could not parse data for type ' + this.type + '.')
    }
  }

  return HyperMedia;

})();

/**
 * A route endpoint.
 */

var Route = (function () {

  function Route (req, defaultBodyMime, middleware) {
    this.req = req;
    this.defaultBodyMime = defaultBodyMime || 'json';
    this.middleware = middleware;
  }

  Route.prototype.get = function (query, next) {
    if (typeof query == 'function') {
      next = query;
      query = null;
    }
    return this.middleware(remutil.modify(remutil.request.url(this.req, {
      query: query || {}
    }), {
      method: 'GET'
    }), next);
  };

  Route.prototype.head = function (query, next) {
    if (typeof query == 'function') {
      next = query;
      query = null;
    }
    return this.middleware(remutil.modify(remutil.request.url(this.req, {
      query: query || {}
    }), {
      method: 'HEAD'
    }), next);
  };

  Route.prototype.post = function (mime, body, next) {
    if (typeof body == 'function') {
      next = body;
      body = mime;
      mime = this.defaultBodyMime;
    }
    return this.middleware(remutil.modify(remutil.request.body(this.req, mime, body), {
      method: 'POST'
    }), next);
  };

  Route.prototype.put = function (mime, body, next) {
    if (typeof body == 'function') {
      next = body;
      body = mime;
      mime = this.defaultBodyMime;
    }
    return this.middleware(remutil.modify(remutil.request.body(this.req, mime, body), {
      method: 'PUT'
    }), next);
  };

  Route.prototype.del = function (next) {
    return this.middleware(remutil.modify(this.req, {
      method: 'DELETE'
    }), next);
  };

  return Route;

})();

/**
 * Middleware.
 */

var Middleware = (function () {

  function Middleware () { }

  Middleware.prototype.pre = function (type, callback) {
    this._middleware || (this._middleware = {});
    (this._middleware[type] || (this._middleware[type] = [])).push(callback);
    return this;
  };

  Middleware.prototype.middleware = function (type) {
    var args = Array.prototype.slice.call(arguments, 1);
    (this._middleware && this._middleware[type] || []).forEach(function (callback) {
      callback.apply(this, args);
    }.bind(this));
    return this;
  };

  return Middleware;

})();

/**
 * API
 */

var API = (function () {

  remutil.inherits(API, Middleware);

  function API (manifest, opts) {
    this.manifest = manifest;
    this.opts = opts || {};

    // Load key, secret, format.
    this.key = this.opts.key;
    this.secret = this.opts.secret;
    this.format = this.opts.format || 'json';

    // Load format-specific options from the manifest.
    if (!this.manifest.formats) {
      this.manifest.formats = {json: {}};
    }
    if (!this.manifest.formats[this.format]) {
      throw new Error("Format \"" + this.format + "\" not available. Please specify an available format in the options parameter.");
    }
    this.manifest = remutil.modify(this.manifest, this.manifest.formats[this.format]);

    // User agent.
    this.pre('request', function (req) {
      req.headers['user-agent'] = req.headers['user-agent'] || rem.USER_AGENT;
    });
    // Route root pathname.
    if (this.manifest.basepath) {
      this.pre('request', function (req) {
        req.url.pathname = this.manifest.basepath + req.url.pathname;
      });
    }
    // Route suffix.
    if (this.manifest.suffix) {
      this.pre('request', function (req) {
        req.url.pathname += this.manifest.suffix;
      });
    }
    // Route configuration parameters.
    if (this.manifest.configParams) {
      this.pre('request', function (req) {
        var params = this.manifest.configParams;
        for (var key in params) {
          req.url.query[key] = this.opts[this.manifest.configParams[key]];
        }
      });
    }
    // Route static parameters.
    if (this.manifest.params) {
      this.pre('request', function (req) {
        var params = this.manifest.params;
        for (var key in params) {
          req.url.query[key] = params[key];
        }
      });
    }
  }

  // Configuration prompt.

  API.prototype.configure = function(cont) {
    return cont();
  };

  // Callable function.

  function invoke (api, segments, send) {
    var query = typeof segments[segments.length - 1] == 'object' ? segments.pop() : {};
    var pathname = remutil.path.join.apply(null, segments);

    return new Route(remutil.request.create({
      query: query, pathname: pathname
    }), api.manifest.uploadFormat, middleware);

    function middleware (req, next) {
      // Expand payload shorthand.
      api.configure(function () {
        // Determine base that matches the path name.
        var pathname = req.url.pathname.replace(/^(?!\/)/, '/')
        // Bases can be fixed or an array of (pattern, base) tuples.
        if (Array.isArray(api.manifest.base)) {
          var base = '';
          api.manifest.base.some(function (tuple) {
            if (typeof tuple == 'string') {
              // TODO this functionality should be removed
              base = tuple;
              return true;
            } else {
              if (pathname.match(new RegExp(tuple[0]))) {
                base = tuple[1];
                return true;
              }
            }
          });
        } else {
          var base = String(api.manifest.base);
        }
        // Update the request with base.
        req = remutil.request.url(req, remutil.url.parse(base))
        req = remutil.request.url(req, {
          pathname: remutil.path.join(req.url.pathname, pathname)
        });

        // Debug flag.
        if (api.debug) {
          console.error('[URL]', remutil.url.format(req.url));
        }

        // Apply manifest filters.
        api.middleware('request', req);
        send(req, next);
      });

      return req;
    }
  }

  API.prototype.stream = function () {
    return invoke(this, Array.prototype.slice.call(arguments), function (req, next) {
      this.send(req, function (err, res) {
        next(req, res);
      });
    }.bind(this));
  };

  API.prototype.call = function () {
    return invoke(this, Array.prototype.slice.call(arguments), function (req, next) {
      this.send(req, function (err, res) {
        if (err) {
          next && next(err, null, res);
        } else {
          remutil.consumeStream(res, function (data) {
            var media = new HyperMedia(this, res, data);
            next && next(media.err, media.data, media);
          }.bind(this));
        }
      }.bind(this));
    }.bind(this));
  };

  API.prototype.send = function (req, next) {
    remutil.request.send(req, next);
  }

  // Root request shorthands.

  API.prototype.get = function () {
    var route = this('');
    return route.get.apply(route, arguments);
  };

  API.prototype.post = function () {
    var route = this('');
    return route.post.apply(route, arguments);
  };

  API.prototype.del = function () {
    var route = this('');
    return route.del.apply(route, arguments);
  };

  API.prototype.head = function () {
    var route = this('');
    return route.head.apply(route, arguments);
  };

  API.prototype.put = function () {
    var route = this('');
    return route.put.apply(route, arguments);
  };

  API.prototype.patch = function () {
    var route = this('');
    return route.patch.apply(route, arguments);
  };

  // Configuration/prompt

  if (typeof require !== 'undefined') {

    API.prototype._promptConfig = false;

    API.prototype._persistConfig = false;

    API.prototype.prompt = function (_persistConfig) {
      this._persistConfig = _persistConfig || _persistConfig == null;
      this._promptConfig = true;
      return this;
    };

    API.prototype.configure = function (cont) {
      //return cont();

      var nconf = require('nconf');
      var read = require('read');
      var clc = require('cli-color');
      var path = require('path');

      // Configuration.
      var configFile = rem.CONFIG_FILE || path.join(require('osenv').home(), '.remconf');
      nconf.file(configFile);

      // Optionally prompt for API key/secret.
      var k, v, _ref, _ref1,
        _this = this;
      if (this.key) {
        return cont();
      }
      if (!(this._promptConfig && this.manifest.id)) {
        throw new Error('No API key specified.');
      }
      if (this._persistConfig && nconf.get(this.manifest.id)) {
        _ref = nconf.get(this.manifest.id);
        for (k in _ref) {
          v = _ref[k];
          this.opts[k] = v;
        }
        _ref1 = this.opts, this.key = _ref1.key, this.secret = _ref1.secret;
        return cont();
      }
      console.log(clc.yellow('Initializing API keys for ' + this.manifest.id + ' on first use.'));
      if (this.manifest.control) {
        console.log(clc.yellow('Register for an API key here:'), this.manifest.control);
      }
      this.middleware('configure');

      read({
        prompt: clc.yellow(this.manifest.id + ' API key: ')
      }, function (err, key) {
        _this.key = key;
        _this.opts.key = key;
        if (!key) {
          console.error(clc.red('ERROR:'), 'No API key specified, aborting.');
          process.exit(1);
        }
        return read({
          prompt: clc.yellow(_this.manifest.id + ' API secret (if provided): ')
        }, function (err, secret) {
          _this.secret = secret;
          _this.opts.secret = secret;
          if (_this._persistConfig) {
            nconf.set(_this.manifest.id + ':key', key);
            nconf.set(_this.manifest.id + ':secret', secret);
            return nconf.save(function (err, json) {
              console.log(clc.yellow('Your credentials are saved to the configuration file ' + configFile));
              console.log(clc.yellow('Edit that file to update or change your credentials.\n'));
              return cont();
            });
          } else {
            console.log('');
            return cont();
          }
        });
      });
    };

  }

  // Throttling.

  API.prototype.throttle = function (rate) {
    var api = this, queue = [], rate = rate || 60;

    setInterval(function () {
      var fn = queue.shift();
      if (fn) {
        fn();
      }
    }, 1000/rate)

    var oldsend = api.send;
    api.send = function () {
      var args = arguments;
      queue.push(function () {
        oldsend.apply(api, args);
      });
    };

    return api;
  };

  // Return.

  return API;

})();

/**
 * Public API.
 */

rem.API = API;

rem.create = function (manifest, opts) {
  return remutil.callable(new API(manifest, opts));
};

// TODO Be able to load manifest files locally.
rem.load = function (name, version, opts) {
  manifest = remutil.lookup(name);
  version = version = '*' ? Number(version) || '*' : '*';
  if (!manifest || !manifest[version]) {
    if (version == '*' && manifest) {
      var version = Object.keys(manifest).sort().pop();
      if (!manifest[version]) {
        throw new Error('Unable to find API ' + JSON.stringify(name) + ' version ' + JSON.stringify(Number(version)) + '. For the latest API, use "*".');
      }
    } else if (manifest) {
      throw new Error('Unable to find API ' + JSON.stringify(name) + ' version ' + JSON.stringify(Number(version)) + '. For the latest API, use "*".');
    } else {
      throw new Error('Unable to find API ' + JSON.stringify(name) + '.');
    }
  }
  manifest = manifest[version];
  manifest.id = name;
  manifest.version = version;
  return rem.create(manifest, opts);
};

rem.url = function () {
  var segments = Array.prototype.slice.call(arguments);
  var query = typeof segments[segments.length - 1] == 'object' ? segments.pop() : {};
  var url = remutil.url.parse(segments.shift());
  url.pathname = remutil.path.join.apply(null, [url.pathname].concat(segments));
  url.query = remutil.modify(url.query, query);

  return new Route(remutil.request.create(url), 'form', function (req, next) {
    req.headers['user-agent'] = req.headers['user-agent'] || rem.USER_AGENT;
    remutil.request.send(req, next);
    return req;
  });
};

rem.consume = remutil.consumeStream;

rem.parsers = {
  xml: function (data) {
    try {
      var libxmljs = require('libxmljs');
    } catch (e) {
      throw new Error('Please install libxmljs in order to parse XML APIs.')
    }
    return libxmljs.parseXmlString(data);
  }
};

function jsonpath (obj, keys) {
  keys.split('.').filter(String).forEach(function (key) {
    obj = obj && obj[key];
  });
  return obj;
}

rem.poll = function (endpoint, opts, callback) {
  // opts is an optional argument with a 'interval', 'root', and 'date' param.
  callback = typeof callback == 'function' ? callback : opts;
  opts = typeof opts == 'object' ? opts : {};
  var interval = opts.interval || 1000;
  var ARRAY_ROOT = opts.root || '';
  var DATE_KEY = opts.date || 'created_at';

  var latest = null;
  setInterval(function () {
    endpoint.get(function (err, json) {
      if (json && jsonpath(json, ARRAY_ROOT)) {
        var root = jsonpath(json, ARRAY_ROOT);
        for (var i = 0; i < root.length; i++) {
          if (latest && new Date(jsonpath(root[i], DATE_KEY)) <= latest) {
            break;
          }
        }
        if (i > 0) {
          var items = root.slice(0, i);
          callback(null, items);
          latest = new Date(jsonpath(items[0], DATE_KEY));
        }
      }
    });
  }, interval);
}

if (typeof require != 'undefined') {
  // Authentication methods.
  require('./oauth');
  require('./aws');
  require('./session');

  // TODO more than Oauth.
  rem.console = rem.oauthConsole;
}
<commit_msg>Throttle properly<commit_after>/*

REM: Remedial Rest Interfaces

A library that simplifies and normalizes access to REST APIs.

Reference:
http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven

*/

// Namespace.
var rem = typeof exports == 'undefined' ? this.rem = {} : exports;
var remutil = typeof require == 'undefined' ? remutil : require('./remutil');

// Configuration.
rem.USER_AGENT = 'Mozilla/5.0 (compatible; REMbot/1.0; +http://rem.tcr.io/)';
rem.CONFIG_FILE = null;

/**
 * A hypermedia resource.
 */

var HyperMedia = (function () {

  function HyperMedia (api, res, data) {
    this.api = api;
    this.res = res;
    this.data = data;

    this.type = api.format;
    this.statusCode = Number(this.res.statusCode);
    this.err = this.statusCode > 400 ? this.statusCode : 0;

    // Parse body
    try {
      if (this.type === 'xml') {
        this.data = this.xml = rem.parsers.xml(String(this.data));
      } else {
        this.data = this.json = JSON.parse(String(this.data));
      }
    } catch (e) {
      this.err = e;
      console.warn('Could not parse data for type ' + this.type + '.')
    }
  }

  return HyperMedia;

})();

/**
 * A route endpoint.
 */

var Route = (function () {

  function Route (req, defaultBodyMime, middleware) {
    this.req = req;
    this.defaultBodyMime = defaultBodyMime || 'json';
    this.middleware = middleware;
  }

  Route.prototype.get = function (query, next) {
    if (typeof query == 'function') {
      next = query;
      query = null;
    }
    return this.middleware(remutil.modify(remutil.request.url(this.req, {
      query: query || {}
    }), {
      method: 'GET'
    }), next);
  };

  Route.prototype.head = function (query, next) {
    if (typeof query == 'function') {
      next = query;
      query = null;
    }
    return this.middleware(remutil.modify(remutil.request.url(this.req, {
      query: query || {}
    }), {
      method: 'HEAD'
    }), next);
  };

  Route.prototype.post = function (mime, body, next) {
    if (typeof body == 'function') {
      next = body;
      body = mime;
      mime = this.defaultBodyMime;
    }
    return this.middleware(remutil.modify(remutil.request.body(this.req, mime, body), {
      method: 'POST'
    }), next);
  };

  Route.prototype.put = function (mime, body, next) {
    if (typeof body == 'function') {
      next = body;
      body = mime;
      mime = this.defaultBodyMime;
    }
    return this.middleware(remutil.modify(remutil.request.body(this.req, mime, body), {
      method: 'PUT'
    }), next);
  };

  Route.prototype.del = function (next) {
    return this.middleware(remutil.modify(this.req, {
      method: 'DELETE'
    }), next);
  };

  return Route;

})();

/**
 * Middleware.
 */

var Middleware = (function () {

  function Middleware () { }

  Middleware.prototype.pre = function (type, callback) {
    this._middleware || (this._middleware = {});
    (this._middleware[type] || (this._middleware[type] = [])).push(callback);
    return this;
  };

  Middleware.prototype.middleware = function (type) {
    var args = Array.prototype.slice.call(arguments, 1);
    (this._middleware && this._middleware[type] || []).forEach(function (callback) {
      callback.apply(this, args);
    }.bind(this));
    return this;
  };

  return Middleware;

})();

/**
 * API
 */

var API = (function () {

  remutil.inherits(API, Middleware);

  function API (manifest, opts) {
    this.manifest = manifest;
    this.opts = opts || {};

    // Load key, secret, format.
    this.key = this.opts.key;
    this.secret = this.opts.secret;
    this.format = this.opts.format || 'json';

    // Load format-specific options from the manifest.
    if (!this.manifest.formats) {
      this.manifest.formats = {json: {}};
    }
    if (!this.manifest.formats[this.format]) {
      throw new Error("Format \"" + this.format + "\" not available. Please specify an available format in the options parameter.");
    }
    this.manifest = remutil.modify(this.manifest, this.manifest.formats[this.format]);

    // User agent.
    this.pre('request', function (req) {
      req.headers['user-agent'] = req.headers['user-agent'] || rem.USER_AGENT;
    });
    // Route root pathname.
    if (this.manifest.basepath) {
      this.pre('request', function (req) {
        req.url.pathname = this.manifest.basepath + req.url.pathname;
      });
    }
    // Route suffix.
    if (this.manifest.suffix) {
      this.pre('request', function (req) {
        req.url.pathname += this.manifest.suffix;
      });
    }
    // Route configuration parameters.
    if (this.manifest.configParams) {
      this.pre('request', function (req) {
        var params = this.manifest.configParams;
        for (var key in params) {
          req.url.query[key] = this.opts[this.manifest.configParams[key]];
        }
      });
    }
    // Route static parameters.
    if (this.manifest.params) {
      this.pre('request', function (req) {
        var params = this.manifest.params;
        for (var key in params) {
          req.url.query[key] = params[key];
        }
      });
    }
  }

  // Configuration prompt.

  API.prototype.configure = function(cont) {
    return cont();
  };

  // Callable function.

  function invoke (api, segments, send) {
    var query = typeof segments[segments.length - 1] == 'object' ? segments.pop() : {};
    var pathname = remutil.path.join.apply(null, segments);

    return new Route(remutil.request.create({
      query: query, pathname: pathname
    }), api.manifest.uploadFormat, middleware);

    function middleware (req, next) {
      // Expand payload shorthand.
      api.configure(function () {
        // Determine base that matches the path name.
        var pathname = req.url.pathname.replace(/^(?!\/)/, '/')
        // Bases can be fixed or an array of (pattern, base) tuples.
        if (Array.isArray(api.manifest.base)) {
          var base = '';
          api.manifest.base.some(function (tuple) {
            if (typeof tuple == 'string') {
              // TODO this functionality should be removed
              base = tuple;
              return true;
            } else {
              if (pathname.match(new RegExp(tuple[0]))) {
                base = tuple[1];
                return true;
              }
            }
          });
        } else {
          var base = String(api.manifest.base);
        }
        // Update the request with base.
        req = remutil.request.url(req, remutil.url.parse(base))
        req = remutil.request.url(req, {
          pathname: remutil.path.join(req.url.pathname, pathname)
        });

        // Debug flag.
        if (api.debug) {
          console.error('[URL]', remutil.url.format(req.url));
        }

        // Apply manifest filters.
        api.middleware('request', req);
        send(req, next);
      });

      return req;
    }
  }

  API.prototype.stream = function () {
    return invoke(this, Array.prototype.slice.call(arguments), function (req, next) {
      this.send(req, function (err, res) {
        next(req, res);
      });
    }.bind(this));
  };

  API.prototype.call = function () {
    return invoke(this, Array.prototype.slice.call(arguments), function (req, next) {
      this.send(req, function (err, res) {
        if (err) {
          next && next(err, null, res);
        } else {
          remutil.consumeStream(res, function (data) {
            var media = new HyperMedia(this, res, data);
            next && next(media.err, media.data, media);
          }.bind(this));
        }
      }.bind(this));
    }.bind(this));
  };

  API.prototype.send = function (req, next) {
    remutil.request.send(req, next);
  }

  // Root request shorthands.

  API.prototype.get = function () {
    var route = this('');
    return route.get.apply(route, arguments);
  };

  API.prototype.post = function () {
    var route = this('');
    return route.post.apply(route, arguments);
  };

  API.prototype.del = function () {
    var route = this('');
    return route.del.apply(route, arguments);
  };

  API.prototype.head = function () {
    var route = this('');
    return route.head.apply(route, arguments);
  };

  API.prototype.put = function () {
    var route = this('');
    return route.put.apply(route, arguments);
  };

  API.prototype.patch = function () {
    var route = this('');
    return route.patch.apply(route, arguments);
  };

  // Configuration/prompt

  if (typeof require !== 'undefined') {

    API.prototype._promptConfig = false;

    API.prototype._persistConfig = false;

    API.prototype.prompt = function (_persistConfig) {
      this._persistConfig = _persistConfig || _persistConfig == null;
      this._promptConfig = true;
      return this;
    };

    API.prototype.configure = function (cont) {
      //return cont();

      var nconf = require('nconf');
      var read = require('read');
      var clc = require('cli-color');
      var path = require('path');

      // Configuration.
      var configFile = rem.CONFIG_FILE || path.join(require('osenv').home(), '.remconf');
      nconf.file(configFile);

      // Optionally prompt for API key/secret.
      var k, v, _ref, _ref1,
        _this = this;
      if (this.key) {
        return cont();
      }
      if (!(this._promptConfig && this.manifest.id)) {
        throw new Error('No API key specified.');
      }
      if (this._persistConfig && nconf.get(this.manifest.id)) {
        _ref = nconf.get(this.manifest.id);
        for (k in _ref) {
          v = _ref[k];
          this.opts[k] = v;
        }
        _ref1 = this.opts, this.key = _ref1.key, this.secret = _ref1.secret;
        return cont();
      }
      console.log(clc.yellow('Initializing API keys for ' + this.manifest.id + ' on first use.'));
      if (this.manifest.control) {
        console.log(clc.yellow('Register for an API key here:'), this.manifest.control);
      }
      this.middleware('configure');

      read({
        prompt: clc.yellow(this.manifest.id + ' API key: ')
      }, function (err, key) {
        _this.key = key;
        _this.opts.key = key;
        if (!key) {
          console.error(clc.red('ERROR:'), 'No API key specified, aborting.');
          process.exit(1);
        }
        return read({
          prompt: clc.yellow(_this.manifest.id + ' API secret (if provided): ')
        }, function (err, secret) {
          _this.secret = secret;
          _this.opts.secret = secret;
          if (_this._persistConfig) {
            nconf.set(_this.manifest.id + ':key', key);
            nconf.set(_this.manifest.id + ':secret', secret);
            return nconf.save(function (err, json) {
              console.log(clc.yellow('Your credentials are saved to the configuration file ' + configFile));
              console.log(clc.yellow('Edit that file to update or change your credentials.\n'));
              return cont();
            });
          } else {
            console.log('');
            return cont();
          }
        });
      });
    };

  }

  // Throttling.

  API.prototype.throttle = function (rate) {
    var api = this, queue = [], rate = rate || 1;

    setInterval(function () {
      var fn = queue.shift();
      if (fn) {
        fn();
      }
    }, 1000/rate)

    var oldsend = api.send;
    api.send = function () {
      var args = arguments;
      queue.push(function () {
        oldsend.apply(api, args);
      });
    };

    return api;
  };

  // Return.

  return API;

})();

/**
 * Public API.
 */

rem.API = API;

rem.create = function (manifest, opts) {
  return remutil.callable(new API(manifest, opts));
};

// TODO Be able to load manifest files locally.
rem.load = function (name, version, opts) {
  manifest = remutil.lookup(name);
  version = version = '*' ? Number(version) || '*' : '*';
  if (!manifest || !manifest[version]) {
    if (version == '*' && manifest) {
      var version = Object.keys(manifest).sort().pop();
      if (!manifest[version]) {
        throw new Error('Unable to find API ' + JSON.stringify(name) + ' version ' + JSON.stringify(Number(version)) + '. For the latest API, use "*".');
      }
    } else if (manifest) {
      throw new Error('Unable to find API ' + JSON.stringify(name) + ' version ' + JSON.stringify(Number(version)) + '. For the latest API, use "*".');
    } else {
      throw new Error('Unable to find API ' + JSON.stringify(name) + '.');
    }
  }
  manifest = manifest[version];
  manifest.id = name;
  manifest.version = version;
  return rem.create(manifest, opts);
};

rem.url = function () {
  var segments = Array.prototype.slice.call(arguments);
  var query = typeof segments[segments.length - 1] == 'object' ? segments.pop() : {};
  var url = remutil.url.parse(segments.shift());
  url.pathname = remutil.path.join.apply(null, [url.pathname].concat(segments));
  url.query = remutil.modify(url.query, query);

  return new Route(remutil.request.create(url), 'form', function (req, next) {
    req.headers['user-agent'] = req.headers['user-agent'] || rem.USER_AGENT;
    remutil.request.send(req, next);
    return req;
  });
};

rem.consume = remutil.consumeStream;

rem.parsers = {
  xml: function (data) {
    try {
      var libxmljs = require('libxmljs');
    } catch (e) {
      throw new Error('Please install libxmljs in order to parse XML APIs.')
    }
    return libxmljs.parseXmlString(data);
  }
};

function jsonpath (obj, keys) {
  keys.split('.').filter(String).forEach(function (key) {
    obj = obj && obj[key];
  });
  return obj;
}

rem.poll = function (endpoint, opts, callback) {
  // opts is an optional argument with a 'interval', 'root', and 'date' param.
  callback = typeof callback == 'function' ? callback : opts;
  opts = typeof opts == 'object' ? opts : {};
  var interval = opts.interval || 1000;
  var ARRAY_ROOT = opts.root || '';
  var DATE_KEY = opts.date || 'created_at';

  var latest = null;
  setInterval(function () {
    endpoint.get(function (err, json) {
      if (json && jsonpath(json, ARRAY_ROOT)) {
        var root = jsonpath(json, ARRAY_ROOT);
        for (var i = 0; i < root.length; i++) {
          if (latest && new Date(jsonpath(root[i], DATE_KEY)) <= latest) {
            break;
          }
        }
        if (i > 0) {
          var items = root.slice(0, i);
          callback(null, items);
          latest = new Date(jsonpath(items[0], DATE_KEY));
        }
      }
    });
  }, interval);
}

if (typeof require != 'undefined') {
  // Authentication methods.
  require('./oauth');
  require('./aws');
  require('./session');

  // TODO more than Oauth.
  rem.console = rem.oauthConsole;
}
<|endoftext|>