<commit_before>
import {cacheFor} from './cache.js';
import {jsdecode} from '../../node_modules/ok-emoji/src/string.js';
import {isSingleValidEmoji} from '../../node_modules/ok-emoji/src/measurer.js';
import * as emoji from '../../node_modules/ok-emoji/src/emoji.js';

/**
 * @param {string} string to measure
 * @return {boolean} whether this is a single emoji long
 */
const isSingle = cacheFor(isSingleValidEmoji);

/**
 * True if the standard "female" icon can be varied with a diversity modifier. This is the basic
 * level of emoji diversity support, but doesn't imply support for all the professions.
 *
 * @type {boolean}
 */
const basicDiversity = isSingle('\u{1f468}\u{1f3fb}');

/**
 * @type {!Array<number>}
 */
const skipDiversityList = [
  0x1f91d,  // handshake
  0x1f46a,  // nuclear family
  0x1f93c,  // people wrestling
  0x1f46b,  // m+w hands
  0x1f46c,  // m+m hands
  0x1f46d,  // w+w hands
];

/**
 * @param {number} p
 * @return {boolean} whether to skip diversity for this point
 */
function skipDiversity(p) {
  return skipDiversityList.indexOf(p) !== -1;
}

/**
 * @param {number} p
 * @return {boolean} whether the passed rune is a gender character
 */
function isPointGender(p) {
  return p === 0x2640 || p === 0x2642;
}

/**
 * @param {number} p
 * @return {boolean} whether the passed rune is a basic emoji person gender: Man or Woman
 */
function isPersonGender(p) {
  return p === 0x1f468 || p === 0x1f469;
}

/**
 * @param {number} p
 * @return {boolean} whether this is a subordinate member of a family sequence: boy/girl/baby
 */
function isFamilyMember(p) {
  return p === 0x1f476 || p === 0x1f466 || p === 0x1f467;
}

/**
 * Returns details about a character and whether it can be gender flipped to a single or neutral
 * gender. This contains emoji which, due to historical emoji reasons, aren't officially versions
 * of each other: e.g., Santa and Mrs. Claus, or Old {Man,Woman,Adult}.
 *
 * @param {number} point to look up in the other gender flips table
 * @return {{single: boolean, neutral: boolean, points: {f: number, m: number, n: number}}?}
 */
const genderFlip = (function() {
  const list = [
    0x1f468, 0x1f469, 0,        // man, woman (normal case)
    0x1f936, 0x1f385, 0,        // mrs. claus, santa
    0x1f483, 0x1f57a, 0,        // dancers
    0x1f470, 0x1f935, 0,        // bride, man in tuxedo
    0x1f467, 0x1f466, 0x1f9d2,  // girl, boy, child
    0x1f475, 0x1f474, 0x1f9d3,  // old {woman,man,adult}
    0x1f46d, 0x1f46c, 0x1f46b,  // women/men holding hands; note this has 'mixed' for neutral
    0x1f478, 0x1f934, 0,        // princess, prince
  ];

  // build map with raw data only
  const all = new Map();
  for (let i = 0; i < list.length; i += 3) {
    const data = {
      points: {f: list[i], m: list[i+1], n: list[i+2]},
    };
    for (let j = 0; j < 3; ++j) {
      const v = list[i+j];
      if (v) {
        if (all.has(v)) {
          throw new Error('duplicate in gender list: ' + v);
        }
        all.set(v, data);
      }
    }
  }

  // return helper
  return (point) => {
    const out = all.get(point) || null;
    if (out && out.single === undefined) {
      // do the heavy lifting only when first fetched
      out.single =
          isSingle(String.fromCodePoint(out.points.f)) &&
          isSingle(String.fromCodePoint(out.points.m));
      out.neutral = out.points.n ? isSingle(String.fromCodePoint(out.points.n)) : false;
    }
    return out;
  };
}());

/**
 * @param {!Array<number>} points
 * @yields {{part: !Array<number>, trailer: boolean}}
 */
function *reverseParts(points) {
  let last = -1;

  // nb. this actually works in reverse
  do {
    // ugh so many off by ones
    const index = points.lastIndexOf(emoji.runeZWJ, last);
    const from = index + 1;
    const to = (last === -1 ? points.length : last + 1);
    last = index - 1;

    const part = points.slice(from, to);
    if (part.length === 0) {
      continue;  // ignore empty char
    }

    const x = {part: points.slice(from, to), trailer: from !== 0};
    yield x;
  } while (last !== -2);
}

/**
 * @param {!Array<number>} points
 * @return {!Array<!Array<number>>}
 */
function splitPoints(points) {
  let last = 0;
  const out = [];

  for (;;) {
    const index = points.indexOf(emoji.runeZWJ, last);
    const to = (index === -1 ? points.length : index);

    const part = points.slice(last, to);
    if (part.length !== 0) {
      out.push(part);
    }

    if (index === -1) {
      break;
    }
    last = index + 1;
  }

  return out;
}

/**
 * Analyses or modifes an emoji string for modifier support: diversity and gender.
 *
 * This might be O(n), due to the cost of measuring every individual character on some platforms.
 *
 * @param {string} s
 * @param {{tone: (undefined|number), gender: (undefined|string)}=} op
 * @return {
 *   out: (string|undefined),
 *   tone: boolean,
 *   gender: {single: boolean, double: boolean, neutral: boolean},
 * }
 */
export function modify(s, op=undefined) {
  const stats = {tone: false, gender: {single: false, double: false, neutral: false}};

  const all = op !== undefined ? [] : null;
outer:
  for (const points of emoji.iterateEmoji(s)) {
    if (all) {
      all.push(points);  // store for 2nd round
    }

    let genderable = 0;
    let familyLike = false;

    for (const {part, trailer} of reverseParts(points)) {
      // check for early exhaustive answer
      if (op === undefined &&
        (stats.tone || !basicDiversity) &&
        stats.gender.neutral && stats.gender.single && stats.gender.double) {
        break outer;
      }

      const first = part[0];

      if (emoji.isFlagPoint(first)) {
        continue;
      }

      const localFamilyMember = isFamilyMember(first)
      if (trailer && localFamilyMember) {
        familyLike = true;   // familyLike is family member in later part of char
      }

      const localPersonGender = isPersonGender(first)
      if (localFamilyMember || localPersonGender) {
        stats.tone = !familyLike && basicDiversity;
        stats.gender.single = true;
        if (localPersonGender && ++genderable >= 2) {
          stats.gender.double = true;
        }
        continue;  // nothing more to find out here
      }

      if (familyLike) {
        continue;  // no more checks to do
      }

      // check for prescribed gender flips
      const flip = genderFlip(first);
      if (flip !== null) {
        stats.gender.single |= flip.single;
        stats.gender.neutral |= flip.neutral;
      }

      // measure if diversity is possible (only on first char)
      if (basicDiversity && !stats.tone && !trailer && !skipDiversity(first)) {
        const cand = String.fromCodePoint(first, 0x1f3fb);
        stats.tone = isSingle(cand);
      }

      // measure if gender is possible
      if (!stats.gender.neutral) {
        const cand = String.fromCodePoint(first, emoji.runeZWJ, 0x2640, emoji.runeVS16);
        const valid = isSingle(cand);
        stats.gender.neutral = valid;
        stats.gender.single = stats.gender.single || valid;
      }
    }
  }

  if (op === undefined) {
    return stats;
  }

  if (op.tone !== undefined && stats.tone) {
    // nb. tones are only ever valid in position 1
    all.forEach((points) => {
      if (emoji.isSkinTone(points[1])) {
        // found one, either replace or clear
        if (op.tone) {
          // great, save in place
          points[1] = op.tone;
        } else {
          const cand = String.fromCodePoint(points[0]);
          if (!isSingle(cand)) {
            points[1] = emoji.runeVS16;  // tone was holding this as emoji
          } else {
            points.splice(1, 1);  // just remove
          }
        }
        return;  // in-place update
      }

      if (!op.tone) {
        // just remove in case one's in a weird place
        const update = points.filter((point) => !emoji.isSkinTone(point));
        points.splice(0, points.length, ...update);
        return;  // in-place update
      }

      if (skipDiversity(points[0])) {
        return;  // skip
      }

      const cand = String.fromCodePoint(points[0], op.tone, ...points.slice(1));
      if (!isSingle(cand)) {
        return;
      }
      points.splice(1, 0, op.tone);
    });
  }

  if (op.gender !== undefined) {
    const splitAll = all.map((points) => splitPoints(points));
    const updateSplitAll = splitAll.map((parts, i) => {
      // check last point first
      const hadTrailingGender = isPointGender(parts[parts.length - 1][0]);

      // look for gendered emoji at start of all and flip
      if (!hadTrailingGender) {
        let localOp = [];
        let foundAny = false;
        for (let i = 0; i < parts.length; ++i) {
          const part = parts[i];
          const first = part[0];

          const flip = genderFlip(first);
          if (!flip) {
            if (i === 0) {
              break;  // genderable will always be first, give up
            }
            continue;
          }

          // if we're out of ops, split for more (this might still be blank, but reset anyway)
          localOp = localOp.length ? localOp : op.gender.split('');
          const next = localOp.shift();
          if (foundAny && (!next || isFamilyMember(first))) {
            break;  // if we've already matched some, and now have no more data / is family, fail
          }

          if (!next && flip.neutral) {
            part[0] = flip.points.n;
          } else if (next && flip.single) {
            part[0] = (next === 'm' ? flip.points.m : flip.points.f);
          }
          foundAny = true;
        }
        if (foundAny) {
          return parts;  // don't try to add a gender modifier
        }

        // now, either change trailing gender or try to add one
        if (!op.gender) {
          return null;  // nothing to do
        }

        // we need to see if it's possible, clone _whole_ emoji
        const run = all[i].slice();
        run.push(emoji.runeZWJ, 0x2640, emoji.runeVS16);
        const cand = String.fromCodePoint(...run);
        if (!isSingle(cand)) {
          return null;
        }
        parts.push([0x2640, emoji.runeVS16]);  // we change this below
      } else if (!op.gender) {
        parts.pop();
        return parts;
      }

      const last = parts[parts.length - 1];
      last[0] = (op.gender[0] === 'm' ? 0x2642 : 0x2640);
      return parts;
    });

    // remerge changed parts
    updateSplitAll.forEach((parts, i) => {
      if (parts === null) {
        return;  // nothing changed here
      }
      const merged = [];
      parts.forEach((part) => {
        merged.push(...part, emoji.runeZWJ);
      });
      merged.pop();  // remove trailing ZWJ
      all[i] = merged;
    });
  }

  stats.out = all.map((points) => String.fromCodePoint(...points)).join('');
  return stats;
}
<commit_msg>fix normal man/woman swap<commit_after>
import {cacheFor} from './cache.js';
import {jsdecode} from '../../node_modules/ok-emoji/src/string.js';
import {isSingleValidEmoji} from '../../node_modules/ok-emoji/src/measurer.js';
import * as emoji from '../../node_modules/ok-emoji/src/emoji.js';

/**
 * @param {string} string to measure
 * @return {boolean} whether this is a single emoji long
 */
const isSingle = cacheFor(isSingleValidEmoji);

/**
 * True if the standard "female" icon can be varied with a diversity modifier. This is the basic
 * level of emoji diversity support, but doesn't imply support for all the professions.
 *
 * @type {boolean}
 */
const basicDiversity = isSingle('\u{1f468}\u{1f3fb}');

/**
 * @type {!Array<number>}
 */
const skipDiversityList = [
  0x1f91d,  // handshake
  0x1f46a,  // nuclear family
  0x1f93c,  // people wrestling
  0x1f46b,  // m+w hands
  0x1f46c,  // m+m hands
  0x1f46d,  // w+w hands
];

/**
 * @param {number} p
 * @return {boolean} whether to skip diversity for this point
 */
function skipDiversity(p) {
  return skipDiversityList.indexOf(p) !== -1;
}

/**
 * @param {number} p
 * @return {boolean} whether the passed rune is a gender character
 */
function isPointGender(p) {
  return p === 0x2640 || p === 0x2642;
}

/**
 * @param {number} p
 * @return {boolean} whether the passed rune is a basic emoji person gender: Man or Woman
 */
function isPersonGender(p) {
  return p === 0x1f468 || p === 0x1f469;
}

/**
 * @param {number} p
 * @return {boolean} whether this is a subordinate member of a family sequence: boy/girl/baby
 */
function isFamilyMember(p) {
  return p === 0x1f476 || p === 0x1f466 || p === 0x1f467;
}

/**
 * Returns details about a character and whether it can be gender flipped to a single or neutral
 * gender. This contains emoji which, due to historical emoji reasons, aren't officially versions
 * of each other: e.g., Santa and Mrs. Claus, or Old {Man,Woman,Adult}.
 *
 * @param {number} point to look up in the other gender flips table
 * @return {{single: boolean, neutral: boolean, points: {f: number, m: number, n: number}}?}
 */
const genderFlip = (function() {
  const list = [
    0x1f469, 0x1f468, 0,        // woman, man (normal case)
    0x1f936, 0x1f385, 0,        // mrs. claus, santa
    0x1f483, 0x1f57a, 0,        // dancers
    0x1f470, 0x1f935, 0,        // bride, man in tuxedo
    0x1f467, 0x1f466, 0x1f9d2,  // girl, boy, child
    0x1f475, 0x1f474, 0x1f9d3,  // old {woman,man,adult}
    0x1f46d, 0x1f46c, 0x1f46b,  // women/men holding hands; note this has 'mixed' for neutral
    0x1f478, 0x1f934, 0,        // princess, prince
  ];

  // build map with raw data only
  const all = new Map();
  for (let i = 0; i < list.length; i += 3) {
    const data = {
      points: {f: list[i], m: list[i+1], n: list[i+2]},
    };
    for (let j = 0; j < 3; ++j) {
      const v = list[i+j];
      if (v) {
        if (all.has(v)) {
          throw new Error('duplicate in gender list: ' + v);
        }
        all.set(v, data);
      }
    }
  }

  // return helper
  return (point) => {
    const out = all.get(point) || null;
    if (out && out.single === undefined) {
      // do the heavy lifting only when first fetched
      out.single =
          isSingle(String.fromCodePoint(out.points.f)) &&
          isSingle(String.fromCodePoint(out.points.m));
      out.neutral = out.points.n ? isSingle(String.fromCodePoint(out.points.n)) : false;
    }
    return out;
  };
}());

/**
 * @param {!Array<number>} points
 * @yields {{part: !Array<number>, trailer: boolean}}
 */
function *reverseParts(points) {
  let last = -1;

  // nb. this actually works in reverse
  do {
    // ugh so many off by ones
    const index = points.lastIndexOf(emoji.runeZWJ, last);
    const from = index + 1;
    const to = (last === -1 ? points.length : last + 1);
    last = index - 1;

    const part = points.slice(from, to);
    if (part.length === 0) {
      continue;  // ignore empty char
    }

    const x = {part: points.slice(from, to), trailer: from !== 0};
    yield x;
  } while (last !== -2);
}

/**
 * @param {!Array<number>} points
 * @return {!Array<!Array<number>>}
 */
function splitPoints(points) {
  let last = 0;
  const out = [];

  for (;;) {
    const index = points.indexOf(emoji.runeZWJ, last);
    const to = (index === -1 ? points.length : index);

    const part = points.slice(last, to);
    if (part.length !== 0) {
      out.push(part);
    }

    if (index === -1) {
      break;
    }
    last = index + 1;
  }

  return out;
}

/**
 * Analyses or modifes an emoji string for modifier support: diversity and gender.
 *
 * This might be O(n), due to the cost of measuring every individual character on some platforms.
 *
 * @param {string} s
 * @param {{tone: (undefined|number), gender: (undefined|string)}=} op
 * @return {
 *   out: (string|undefined),
 *   tone: boolean,
 *   gender: {single: boolean, double: boolean, neutral: boolean},
 * }
 */
export function modify(s, op=undefined) {
  const stats = {tone: false, gender: {single: false, double: false, neutral: false}};

  const all = op !== undefined ? [] : null;
outer:
  for (const points of emoji.iterateEmoji(s)) {
    if (all) {
      all.push(points);  // store for 2nd round
    }

    let genderable = 0;
    let familyLike = false;

    for (const {part, trailer} of reverseParts(points)) {
      // check for early exhaustive answer
      if (op === undefined &&
        (stats.tone || !basicDiversity) &&
        stats.gender.neutral && stats.gender.single && stats.gender.double) {
        break outer;
      }

      const first = part[0];

      if (emoji.isFlagPoint(first)) {
        continue;
      }

      const localFamilyMember = isFamilyMember(first)
      if (trailer && localFamilyMember) {
        familyLike = true;   // familyLike is family member in later part of char
      }

      const localPersonGender = isPersonGender(first)
      if (localFamilyMember || localPersonGender) {
        stats.tone = !familyLike && basicDiversity;
        stats.gender.single = true;
        if (localPersonGender && ++genderable >= 2) {
          stats.gender.double = true;
        }
        continue;  // nothing more to find out here
      }

      if (familyLike) {
        continue;  // no more checks to do
      }

      // check for prescribed gender flips
      const flip = genderFlip(first);
      if (flip !== null) {
        stats.gender.single |= flip.single;
        stats.gender.neutral |= flip.neutral;
      }

      // measure if diversity is possible (only on first char)
      if (basicDiversity && !stats.tone && !trailer && !skipDiversity(first)) {
        const cand = String.fromCodePoint(first, 0x1f3fb);
        stats.tone = isSingle(cand);
      }

      // measure if gender is possible
      if (!stats.gender.neutral) {
        const cand = String.fromCodePoint(first, emoji.runeZWJ, 0x2640, emoji.runeVS16);
        const valid = isSingle(cand);
        stats.gender.neutral = valid;
        stats.gender.single = stats.gender.single || valid;
      }
    }
  }

  if (op === undefined) {
    return stats;
  }

  if (op.tone !== undefined && stats.tone) {
    // nb. tones are only ever valid in position 1
    all.forEach((points) => {
      if (emoji.isSkinTone(points[1])) {
        // found one, either replace or clear
        if (op.tone) {
          // great, save in place
          points[1] = op.tone;
        } else {
          const cand = String.fromCodePoint(points[0]);
          if (!isSingle(cand)) {
            points[1] = emoji.runeVS16;  // tone was holding this as emoji
          } else {
            points.splice(1, 1);  // just remove
          }
        }
        return;  // in-place update
      }

      if (!op.tone) {
        // just remove in case one's in a weird place
        const update = points.filter((point) => !emoji.isSkinTone(point));
        points.splice(0, points.length, ...update);
        return;  // in-place update
      }

      if (skipDiversity(points[0])) {
        return;  // skip
      }

      const cand = String.fromCodePoint(points[0], op.tone, ...points.slice(1));
      if (!isSingle(cand)) {
        return;
      }
      points.splice(1, 0, op.tone);
    });
  }

  if (op.gender !== undefined) {
    const splitAll = all.map((points) => splitPoints(points));
    const updateSplitAll = splitAll.map((parts, i) => {
      // check last point first
      const hadTrailingGender = isPointGender(parts[parts.length - 1][0]);

      // look for gendered emoji at start of all and flip
      if (!hadTrailingGender) {
        let localOp = [];
        let foundAny = false;
        for (let i = 0; i < parts.length; ++i) {
          const part = parts[i];
          const first = part[0];

          const flip = genderFlip(first);
          if (!flip) {
            if (i === 0) {
              break;  // genderable will always be first, give up
            }
            continue;
          }

          // if we're out of ops, split for more (this might still be blank, but reset anyway)
          localOp = localOp.length ? localOp : op.gender.split('');
          const next = localOp.shift();
          if (foundAny && (!next || isFamilyMember(first))) {
            break;  // if we've already matched some, and now have no more data / is family, fail
          }

          if (!next && flip.neutral) {
            part[0] = flip.points.n;
          } else if (next && flip.single) {
            part[0] = (next === 'm' ? flip.points.m : flip.points.f);
          }
          foundAny = true;
        }
        if (foundAny) {
          return parts;  // don't try to add a gender modifier
        }

        // now, either change trailing gender or try to add one
        if (!op.gender) {
          return null;  // nothing to do
        }

        // we need to see if it's possible, clone _whole_ emoji
        const run = all[i].slice();
        run.push(emoji.runeZWJ, 0x2640, emoji.runeVS16);
        const cand = String.fromCodePoint(...run);
        if (!isSingle(cand)) {
          return null;
        }
        parts.push([0x2640, emoji.runeVS16]);  // we change this below
      } else if (!op.gender) {
        parts.pop();
        return parts;
      }

      const last = parts[parts.length - 1];
      last[0] = (op.gender[0] === 'm' ? 0x2642 : 0x2640);
      return parts;
    });

    // remerge changed parts
    updateSplitAll.forEach((parts, i) => {
      if (parts === null) {
        return;  // nothing changed here
      }
      const merged = [];
      parts.forEach((part) => {
        merged.push(...part, emoji.runeZWJ);
      });
      merged.pop();  // remove trailing ZWJ
      all[i] = merged;
    });
  }

  stats.out = all.map((points) => String.fromCodePoint(...points)).join('');
  return stats;
}
<|endoftext|>


<commit_before>/**
 * Module dependencies.
 */
var util = require('util')
  , OAuth2Strategy = require('passport-oauth').OAuth2Strategy;

/**
 * `Strategy` constructor.
 *
 * The ArmorText authentication strategy authenticates requests by delegating
 * to ArmorText using the OAuth 2.0 protocol.
 *
 * Applications must supply a `verify` callback which accepts an `accessToken`,
 * `refreshToken` and service-specific `profile`, and then calls the `done`
 * callback supplying a `user`, which should be set to `false` if the
 * credentials are not valid.  If an exception occured, `err` should be set.
 *
 * Options:
 *   - `clientID`      your ArmorText application's client id
 *   - `clientSecret`  your ArmorText application's client secret
 *   - `callbackURL`   URL to which ArmorText will redirect the user after granting authorization
 *
 * Examples:
 *
 *     passport.use(new ArmorTextStrategy({
 *         clientID: '123-456-789',
 *         clientSecret: 'shhh-its-a-secret'
 *         callbackURL: 'https://www.example.net/auth/armortext/callback'
 *       },
 *       function(accessToken, refreshToken, profile, done) {
 *         User.findOrCreate(..., function (err, user) {
 *           done(err, user);
 *         });
 *       }
 *     ));
 *
 * @param {Object} options
 * @param {Function} verify
 * @api public
 */
function Strategy(options, verify) {
  options = options || {};
  options.authorizationURL = options.authorizationURL || 'https://app.armortext.co/oauth/authorize';
  options.tokenURL = options.tokenURL || 'https://app.armortext.co/oauth/token';
  this.profileURL = options.profileURL || "https://app.armortext.co/api/v1/users/me/profile";

  OAuth2Strategy.call(this, options, verify);
  this.name = 'armortext';
}

/**
 * Inherit from `OAuth2Strategy`.
 */
util.inherits(Strategy, OAuth2Strategy);

/**
 * Retrieve user profile from ArmorText.
 *
 * This function constructs a normalized profile, with the following properties:
 *
 *   - `provider`         always set to `armortext`
 *   - `id`               unique identifier for this user.
 *   - `username`         the user's ArmorText username
 *   - `displayName`      the user's auth username
 *   - `name.familyName`  user's last name
 *   - `name.givenName`   user's first name
 *   - `gender`           the user's gender: `male` or `female`
 *   - `emails`           the proxied or contact email address granted by the user
 *
 * @param {String} accessToken
 * @param {Function} done
 * @api protected
 */
Strategy.prototype.userProfile = function(accessToken, done) {
  this._oauth2.useAuthorizationHeaderforGET(true);

  this._oauth2.get(this.profileURL, accessToken, function (err, body, res) {
    if (err) { return done(err); }

    try {
      var json = JSON.parse(body);

      var profile = { provider: 'armortext' };

      profile.id = json.id;
      profile.username = json.login;
      profile.displayName = json.first_name + ' ' + json.last_name;
      profile.name = {familyName: json.first_name, givenName: json.last_name};
      profile.gender = json.gender;
      profile.emails = [{ value: json.email }];

      profile._raw = body;
      profile._json = json;
      done(null, profile);
    } catch(e) {
      done(e);
    }
  });
}

/** The default oauth2 strategy puts the access_token into Authorization: header AND query string
  * witch is a violation of the RFC so lets override and not add the header and supply only the token for qs.
  */
Strategy.prototype.get = function(url, access_token, callback) {
  this._oauth2._request("GET", url, {}, "", access_token, callback );
};

/**
 * Expose `Strategy`.
 */
module.exports = Strategy;<commit_msg>added extraction of data from response structure<commit_after>/**
 * Module dependencies.
 */
var util = require('util')
  , OAuth2Strategy = require('passport-oauth').OAuth2Strategy;

/**
 * `Strategy` constructor.
 *
 * The ArmorText authentication strategy authenticates requests by delegating
 * to ArmorText using the OAuth 2.0 protocol.
 *
 * Applications must supply a `verify` callback which accepts an `accessToken`,
 * `refreshToken` and service-specific `profile`, and then calls the `done`
 * callback supplying a `user`, which should be set to `false` if the
 * credentials are not valid.  If an exception occured, `err` should be set.
 *
 * Options:
 *   - `clientID`      your ArmorText application's client id
 *   - `clientSecret`  your ArmorText application's client secret
 *   - `callbackURL`   URL to which ArmorText will redirect the user after granting authorization
 *
 * Examples:
 *
 *     passport.use(new ArmorTextStrategy({
 *         clientID: '123-456-789',
 *         clientSecret: 'shhh-its-a-secret'
 *         callbackURL: 'https://www.example.net/auth/armortext/callback'
 *       },
 *       function(accessToken, refreshToken, profile, done) {
 *         User.findOrCreate(..., function (err, user) {
 *           done(err, user);
 *         });
 *       }
 *     ));
 *
 * @param {Object} options
 * @param {Function} verify
 * @api public
 */
function Strategy(options, verify) {
  options = options || {};
  options.authorizationURL = options.authorizationURL || 'https://app.armortext.co/oauth/authorize';
  options.tokenURL = options.tokenURL || 'https://app.armortext.co/oauth/token';
  this.profileURL = options.profileURL || "https://app.armortext.co/api/v1/users/me/profile";

  OAuth2Strategy.call(this, options, verify);
  this.name = 'armortext';
}

/**
 * Inherit from `OAuth2Strategy`.
 */
util.inherits(Strategy, OAuth2Strategy);

/**
 * Retrieve user profile from ArmorText.
 *
 * This function constructs a normalized profile, with the following properties:
 *
 *   - `provider`         always set to `armortext`
 *   - `id`               unique identifier for this user.
 *   - `username`         the user's ArmorText username
 *   - `displayName`      the user's auth username
 *   - `name.familyName`  user's last name
 *   - `name.givenName`   user's first name
 *   - `gender`           the user's gender: `male` or `female`
 *   - `emails`           the proxied or contact email address granted by the user
 *
 * @param {String} accessToken
 * @param {Function} done
 * @api protected
 */
Strategy.prototype.userProfile = function(accessToken, done) {
  this._oauth2.useAuthorizationHeaderforGET(true);

  this._oauth2.get(this.profileURL, accessToken, function (err, body, res) {
    if (err) { return done(err); }

    try {
      var json = JSON.parse(body);

      var profile = { provider: 'armortext' };

      var data = json.data;

      profile.id = data._id;
      profile.username = data.login;
      profile.displayName = data.first_name + ' ' + data.last_name;
      profile.name = {familyName: data.first_name, givenName: data.last_name};
      profile.gender = data.gender;
      profile.emails = [{ value: data.email }];

      profile._raw = JSON.stringify(data);
      profile._json = data;
      done(null, profile);
    } catch(e) {
      done(e);
    }
  });
}

/** The default oauth2 strategy puts the access_token into Authorization: header AND query string
  * witch is a violation of the RFC so lets override and not add the header and supply only the token for qs.
  */
Strategy.prototype.get = function(url, access_token, callback) {
  this._oauth2._request("GET", url, {}, "", access_token, callback );
};

/**
 * Expose `Strategy`.
 */
module.exports = Strategy;<|endoftext|>


<commit_before>import test from 'ava';
import Tree from '../';

test('should function correctly given a non-reverse customCompare', function (t) {
  var tree = new Tree(function (a, b) {
    return b - a;
  });
  tree.insert(2);
  tree.insert(1);
  tree.insert(3);
  t.is(tree.size(), 3);
  t.is(tree.findMinimum(), 3);
  t.is(tree.findMaximum(), 1);
  tree.delete(3);
  t.is(tree.size(), 2);
  t.is(tree._root.key, 2);
  t.is(tree._root.left, null);
  t.is(tree._root.right.key, 1);
});

test('should function correctly given a non-reverse rangeCompare', function (t) {
  const tree = new Tree((a, b) => {
    let result = 0;
    if (
      (a.end <= b.end && a.start >= b.start) ||
      (b.end <= a.end && b.start >= a.start)
    ) {
      result = 0;
    }
    if (a.end > b.end) {
      result = 1;
    }
    if (a.start < b.start) {
      result = -1;
    }
    return result;
  });
  tree.insert({start: 16777472, end: 16778239});
  tree.insert({start: 16779264, end: 16781311});
  t.true(tree.contains({start: 16777473, end: 16777473}));
  t.false(tree.contains({start: 16781312, end: 16781312}));
});
<commit_msg>Support complex object keys<commit_after>import test from 'ava';
import Tree from '../';

test('should function correctly given a non-reverse customCompare', function (t) {
  var tree = new Tree(function (a, b) {
    return b - a;
  });
  tree.insert(2);
  tree.insert(1);
  tree.insert(3);
  t.is(tree.size(), 3);
  t.is(tree.findMinimum(), 3);
  t.is(tree.findMaximum(), 1);
  tree.delete(3);
  t.is(tree.size(), 2);
  t.is(tree._root.key, 2);
  t.is(tree._root.left, null);
  t.is(tree._root.right.key, 1);
});

test('should work when the key is a complex object', function (t) {
  const tree = new Tree((a, b) => {
    return a.innerKey - b.innerKey;
  });
  tree.insert({innerKey: 1});
  t.true(tree.contains({innerKey: 1}));
  t.false(tree.contains({innerKey: 2}));
});
<|endoftext|>


<commit_before>var offset = 0;

$(document).ready(function () {
    loadProducts(offset);
});

$(window).scroll(function () {
        if ($(window).scrollTop() + $(window).height() > $(document).height() - 50) {
            offset += 8;
            loadProducts(offset);
        }
    }
);

function filteredProducts() {
    offset = 0;
    loadProducts(offset);
}

function loadProducts(offset) {
    var now = new Date().toISOString().slice(0, 19).replace('T', ' ');
    var xhttp = new XMLHttpRequest();
    var productsWindow = document.getElementById("productsWindow");
    var loading = document.createElement("img");
    loading.setAttribute("src", "../../web/assets/images/ajax-loader.gif");
    loading.setAttribute("class", "center-block");
    var loaderDiv = document.getElementById("loader");
    if (loaderDiv.children.length < 1) {
        loaderDiv.appendChild(loading);
    }
    xhttp.onreadystatechange = function () {
        if (this.status == 200 && this.readyState == 4) {
            loaderDiv.removeChild(loading);
            if (offset == 0) {
                productsWindow.innerHTML = "";
            }
            var products = JSON.parse(this.responseText);
            if (products.length == 0) {
                $(window).unbind('scroll');
            }
            var i = 0;
            var content = '';
            for (var key in products) {
                if (products.hasOwnProperty(key)) {

                    if (products[key]['percent'] != null && products[key]['start_date'] <= now
                        && products[key]['end_date'] >= now) {
                        var promotedPrice = Math.round((products[key]['price'] -
                                    ((products[key]['price'] * products[key]['percent']) / 100)
                                ) * 100) / 100;
                    } else {
                        promotedPrice = null;
                    }

                    if (key == 0) {
                        content += '
';
                    }

                    if (i == 4) {
                        content +=
                            '

' +
                            '
';

                        i = 0;
                    }

                    content +=
                        '
' +
                        '
' +
                        '' +
                        '' +
                        '' +
                        '
' + products[key]['title'] + '
' +
                        '
' +
                        '&nbspAdd' +
                        '&nbsp&nbsp';
                    if (promotedPrice != null) {
                        content +=
                            '$' + promotedPrice + '' +
                            '$' + products[key]['price'] + '';
                    }
                    else {
                        content +=
                            '$' + products[key]['price'] + '';
                    }

                    content +=
                        '
' +
                        '
' +
                        'View' +
                        '
' +
                        '
' +
                        '
';

                    if (key == products.length - 1) {
                        content +=
                            '

';
                        $('#productsWindow').append(content);
                    }

                    i++;
                }
            }
        }
    };
    var filter = document.getElementById('filter').value;
    var subcid = location.search;
    xhttp.open("GET", "../../controller/products/products_by_category_controller.php?offset="
        + offset + "&filter=" + filter + "&subcid=" + subcid, true);
    xhttp.send();
}<commit_msg>fixed the onScrollToBottom the right way<commit_after>var offset = 0;

$(document).ready(function () {
    loadProducts(offset);
});

$(window).scroll(function () {
        onScrollToBottom();
    }
);

function filteredProducts() {
    $(window).bind('scroll', function () {
        onScrollToBottom();
    });
    offset = 0;
    loadProducts(offset);
}

function onScrollToBottom() {
    if ($(window).scrollTop() + $(window).height() > $(document).height() - 50) {
        offset += 8;
        loadProducts(offset);
    }
}

function loadProducts(offset) {
    var now = new Date().toISOString().slice(0, 19).replace('T', ' ');
    var xhttp = new XMLHttpRequest();
    var productsWindow = document.getElementById("productsWindow");
    var loading = document.createElement("img");
    loading.setAttribute("src", "../../web/assets/images/ajax-loader.gif");
    loading.setAttribute("class", "center-block");
    var loaderDiv = document.getElementById("loader");
    if (loaderDiv.children.length < 1) {
        loaderDiv.appendChild(loading);
    }
    xhttp.onreadystatechange = function () {
        if (this.status == 200 && this.readyState == 4) {
            loaderDiv.removeChild(loading);
            if (offset == 0) {
                productsWindow.innerHTML = "";
            }
            var products = JSON.parse(this.responseText);
            if (products.length == 0) {
                $(window).unbind('scroll');
            }
            var i = 0;
            var content = '';
            for (var key in products) {
                if (products.hasOwnProperty(key)) {

                    if (products[key]['percent'] != null && products[key]['start_date'] <= now
                        && products[key]['end_date'] >= now) {
                        var promotedPrice = Math.round((products[key]['price'] -
                                    ((products[key]['price'] * products[key]['percent']) / 100)
                                ) * 100) / 100;
                    } else {
                        promotedPrice = null;
                    }

                    if (key == 0) {
                        content += '
';
                    }

                    if (i == 4) {
                        content +=
                            '

' +
                            '
';

                        i = 0;
                    }

                    content +=
                        '
' +
                        '
' +
                        '' +
                        '' +
                        '' +
                        '
' + products[key]['title'] + '
' +
                        '
' +
                        '&nbspAdd' +
                        '&nbsp&nbsp';
                    if (promotedPrice != null) {
                        content +=
                            '$' + promotedPrice + '' +
                            '$' + products[key]['price'] + '';
                    }
                    else {
                        content +=
                            '$' + products[key]['price'] + '';
                    }

                    content +=
                        '
' +
                        '
' +
                        'View' +
                        '
' +
                        '
' +
                        '
';

                    if (key == products.length - 1) {
                        content +=
                            '

';
                        $('#productsWindow').append(content);
                    }

                    i++;
                }
            }
        }
    };
    var filter = document.getElementById('filter').value;
    var subcid = location.search;
    xhttp.open("GET", "../../controller/products/products_by_category_controller.php?offset="
        + offset + "&filter=" + filter + "&subcid=" + subcid, true);
    xhttp.send();
}<|endoftext|>


<commit_before>var uploadUrl = 'api/admin/upload/';
var mdEditor;
$(function () {
    $(".select2_single").select2({
        minimumResultsForSearch: -1,
        allowClear: true,
        dropdownParent: $("#type-select-parent")
    });

    $("#right_col").on('resize', function () {
        thumbnailImgResize();
    });

    function thumbnailImgResize() {
        var rightWidth = $("#right_col").width();
        if (rightWidth < 660) {
            $("#thumbnail-img").width(rightWidth - 10);
            $("#thumbnail-img").height(((rightWidth - 10) / 660) * 192);
            $("#camera-icon").css("margin-top", 20);
        } else {
            $("#camera-icon").css("margin-top", 80);
        }
    }

    jQuery(window).resize(thumbnailImgResize);
    var timer = setInterval(thumbnailImgResize, 100);

    var keywordsEl = $("#keywords");
    keywordsEl.tagsInput({
        height: '68px',
        width: 'auto'
    });
    keywordsEl.importTags($("#keywordsVal").val());
    refreshKeywords();

    function zeroPad(num, places) {
        var zero = places - num.toString().length + 1;
        return Array(+(zero > 0 && zero)).join("0") + num;
    }

    var editormdTheme = $("#markdown").attr("editormdTheme");
    var dark = editormdTheme === 'dark';
    mdEditor = editormd("editormd", {
        width: "100%",
        height: 840,
        path: editorMdPath,
        toolbarIcons: function () {
            return ["undo", "redo", "|", "bold", "del", "italic", "quote", "|", "h1", "h2", "h3", "h4", "|", "list-ul", "list-ol", "hr", "|", "link", "reference-link", "image", "file", "video", "code", "preformatted-text", "code-block", "table", "datetime", "emoji", "html-entities", "pagebreak", "|", "goto-line", "watch", "fullscreen", "search", "|", "help", "info"]
        },
        toolbarCustomIcons: {
            file: '',
            video: ''
        },
        codeFold: true,
        appendMarkdown: $("#markdown").val(),
        saveHTMLToTextarea: true,
        searchReplace: true,
        htmlDecode: "iframe,pre",
        emoji: true,
        taskList: true,
        tocm: true,         // Using [TOCM]
        tex: true,                   // 开启科学公式TeX语言支持，默认关闭
        flowChart: true,             // 开启流程图支持，默认关闭
        sequenceDiagram: true,       // 开启时序/序列图支持，默认关闭,
        dialogMaskOpacity: 0,    // 设置透明遮罩层的透明度，全局通用，默认值为0.1
        dialogMaskBgColor: "#000", // 设置透明遮罩层的背景颜色，全局通用，默认为#fff
        imageUpload: true,
        imageFormats: ["jpg", "jpeg", "gif", "png", "ico", "bmp", "webp"],
        imageUploadURL: uploadUrl,
        theme: dark ? "dark" : "default",
        previewTheme: dark ? "dark" : "default",
        editorTheme: dark ? "pastel-on-dark" : "default",

        onchange: function () {
            var content = mdEditor.getPreviewedHTML();
            $("#content").val(content);
            if (content === '') {
                $("#editormd").addClass("has-error");
                $("#editormd").css("border-color", "#a94442");
            } else {
                $("#editormd").removeClass("has-error");
                $("#editormd").css("border-color", "#ccc");
            }
        },
        onload: function () {
            $("#content").val(mdEditor.getPreviewedHTML());
            var keyMap = {
                "Ctrl-S": function () {
                    save(true, false);
                }
            };
            this.addKeyMap(keyMap);
            $("#fileDialog").on("click", function () {
                mdEditor.executePlugin("fileDialog", "../plugins/file-dialog/file-dialog");
            });
            $("#videoDialog").on("click", function () {
                mdEditor.executePlugin("videoDialog", "../plugins/video-dialog/video-dialog");
            });
            setInterval(function () {
                save(true, true);
            }, 1000 * 6);
        },
        onfullscreen: function () {
            $("#editormd").css("z-index", "9999");
            mdEditor.width("100%");
            if (screenfull.enabled) {
                screenfull.request();
            }
        },

        onfullscreenExit: function () {
            $("#editormd").css("z-index", 0);
            mdEditor.width("100%");
            if (screenfull.enabled) {
                screenfull.exit();
            }
        }

    });

    $(".editormd-markdown-textarea").attr("name", "mdContent");
    $(".editormd-html-textarea").removeAttr("name");

    function checkPreviewLink() {
        if ($("#id").val() === null || $("#id").val() === '') {
            $("#preview").attr("disable", "disable");
        } else {
            updatePreviewLink($("#id").val());
        }
    }

    checkPreviewLink();

    function updatePreviewLink(id) {
        $("#preview-link").attr("href", "admin/article/preview?id=" + id);
        $("#preview").removeClass("btn-");
        $("#preview-link").show();
    }

    function tips(data, message) {
        PNotify.removeAll();
        if (data.error === 0) {
            new PNotify({
                title: message,
                type: 'success',
                hide: true,
                delay: 3000,
                styling: 'bootstrap3'
            });
            $("#id").val(data.id);
            $("#alias").val(data.alias);
            $("#digest").val(data.digest);
            if (data.thumbnail !== null) {
                fillThumbnail(data.thumbnail);
            }
            updatePreviewLink(data.id);
            if (window.location.href.indexOf('?') === -1) {
                window.history.replaceState({}, "", window.location.pathname + "?id=" + data.id);
            }
        } else {
            new PNotify({
                title: data.message,
                type: 'error',
                hide: true,
                delay: 3000,
                styling: 'bootstrap3'
            });
        }
    }

    var saving = false;
    var lastChangeRequestBody;

    function validator(el) {
        if ($("#title").val() === '') {
            $("#title-parent").addClass("has-error");
        }
        if ($("#content").val() === '') {
            $("#editormd").addClass("has-error");
            $("#editormd").css("border-color", "#a94442");
        }
        return el.find(".has-error").length === 0;
    }

    $("#title").on("change keyup paste click", function () {
        if ($(this).val() !== '') {
            $("#title-parent").removeClass("has-error");
        } else {
            $("#title-parent").addClass("has-error");
        }
    });

    function save(rubbish, timer) {
        //如果是还在保存文章状态，跳过保存
        if (saving) {
            PNotify.removeAll();
            new PNotify({
                title: lang.saving,
                delay: 3000,
                type: 'warn',
                hide: true,
                styling: 'bootstrap3'
            });
            return;
        }
        refreshKeywords();
        var body = getFormRequestBody("#article-form");
        var tLastChangeRequestBody = JSON.stringify(body);
        var changed = tLastChangeRequestBody !== lastChangeRequestBody;
        if (validator($("#article-form")) && (!timer || changed)) {
            body['rubbish'] = rubbish;
            var url;
            if ($("#id").val() !== '') {
                url = "api/admin/article/update";
            } else {
                url = "api/admin/article/create";
            }
            saving = true;
            if (!skipFirstRubbishSave) {
                $.ajax({
                        url: url,
                        data: JSON.stringify(body),
                        method: "POST",
                        dataType: "json",
                        contentType: "application/json",
                        success: function (data) {
                            var date = new Date();
                            saving = false;
                            tips(data, (timer ? lang.auto : "") + (rubbish ? lang.rubbish : "") + " " + lang.saveSuccess + " " + zeroPad(date.getHours(), 2) + ":" + zeroPad(date.getMinutes(), 2) + ":" + zeroPad(date.getSeconds(), 2));
                            lastChangeRequestBody = JSON.stringify(getFormRequestBody("#article-form"));
                        },
                        error: function () {
                            saving = true;
                        }
                    }
                )
            } else {
                skipFirstRubbishSave = false;
                saving = false;
                lastChangeRequestBody = JSON.stringify(getFormRequestBody("#article-form"));
            }
        } else {
            lastChangeRequestBody = JSON.stringify(getFormRequestBody("#article-form"));
        }
    }

    $(document.body).on('click', '#unCheckedTag .tag2', function (e) {
        $("#keywords_tagsinput").find('span[val=' + $(this).text() + ']').remove();
        keywordsEl.importTags($(this).text());
        $(this).remove();
        e.preventDefault();
        refreshKeywords();
    });
    $(document.body).on('click', "#keywords_tagsinput .tag2 a", function () {
        var text = $(this).siblings().text().trim();
        $("#unCheckedTag").find('span[val=' + text + ']').remove();
        $(this).parent().remove();
        $("#unCheckedTag").append('' + text + '');
        refreshKeywords();
        return false;
    });

    function refreshKeywords() {
        var ts = $("#keywords_tagsinput .tag2").children("span");
        var keywordsVal = "";
        for (var i = 0; i < ts.length; i++) {
            keywordsVal = keywordsVal + $(ts[i]).text().trim() + ",";
        }
        $("#keywordsVal").val(keywordsVal);
    }

    $("#saveToRubbish").click(function () {
        save(true, false)
    });

    $("#save").click(function () {
        save(false, false);
    });

    $('#thumbnail-upload').liteUploader({
        script: 'api/admin/upload/thumbnail?dir=thumbnail'
    }).on('lu:success', function (e, response) {
        if (response.error) {
            alert(response.message);
        } else {
            fillThumbnail(response.url);
        }
    });

    function fillThumbnail(url) {
        $("#thumbnail-img").css('background-image', "url('" + url + "')");
        var w = gup("w", url);
        var h = gup("h", url);
        if (h) {
            if (w < 660) {
                h = 660.0 / w * h;
            }
            $("#thumbnail-img").height(h);
            $("#thumbnail").val(url);
            $("#camera-icon").hide();
        }
    }

    var thumbnailImg = $("input[name=\"thumbnail\"]").val();
    if (thumbnailImg !== "") {
        fillThumbnail(thumbnailImg);
    }

});

function gup(name, url) {
    if (!url) url = location.href;
    name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]");
    var regexS = "[\\?&]" + name + "=([^&#]*)";
    var regex = new RegExp(regexS);
    var results = regex.exec(url);
    return results === null ? null : results[1];
}<commit_msg>Fix when edit model save btn not working in first time<commit_after>var uploadUrl = 'api/admin/upload/';
var mdEditor;
$(function () {
    $(".select2_single").select2({
        minimumResultsForSearch: -1,
        allowClear: true,
        dropdownParent: $("#type-select-parent")
    });

    $("#right_col").on('resize', function () {
        thumbnailImgResize();
    });

    function thumbnailImgResize() {
        var rightWidth = $("#right_col").width();
        if (rightWidth < 660) {
            $("#thumbnail-img").width(rightWidth - 10);
            $("#thumbnail-img").height(((rightWidth - 10) / 660) * 192);
            $("#camera-icon").css("margin-top", 20);
        } else {
            $("#camera-icon").css("margin-top", 80);
        }
    }

    jQuery(window).resize(thumbnailImgResize);
    var timer = setInterval(thumbnailImgResize, 100);

    var keywordsEl = $("#keywords");
    keywordsEl.tagsInput({
        height: '68px',
        width: 'auto'
    });
    keywordsEl.importTags($("#keywordsVal").val());
    refreshKeywords();

    function zeroPad(num, places) {
        var zero = places - num.toString().length + 1;
        return Array(+(zero > 0 && zero)).join("0") + num;
    }

    var editormdTheme = $("#markdown").attr("editormdTheme");
    var dark = editormdTheme === 'dark';
    mdEditor = editormd("editormd", {
        width: "100%",
        height: 840,
        path: editorMdPath,
        toolbarIcons: function () {
            return ["undo", "redo", "|", "bold", "del", "italic", "quote", "|", "h1", "h2", "h3", "h4", "|", "list-ul", "list-ol", "hr", "|", "link", "reference-link", "image", "file", "video", "code", "preformatted-text", "code-block", "table", "datetime", "emoji", "html-entities", "pagebreak", "|", "goto-line", "watch", "fullscreen", "search", "|", "help", "info"]
        },
        toolbarCustomIcons: {
            file: '',
            video: ''
        },
        codeFold: true,
        appendMarkdown: $("#markdown").val(),
        saveHTMLToTextarea: true,
        searchReplace: true,
        htmlDecode: "iframe,pre",
        emoji: true,
        taskList: true,
        tocm: true,         // Using [TOCM]
        tex: true,                   // 开启科学公式TeX语言支持，默认关闭
        flowChart: true,             // 开启流程图支持，默认关闭
        sequenceDiagram: true,       // 开启时序/序列图支持，默认关闭,
        dialogMaskOpacity: 0,    // 设置透明遮罩层的透明度，全局通用，默认值为0.1
        dialogMaskBgColor: "#000", // 设置透明遮罩层的背景颜色，全局通用，默认为#fff
        imageUpload: true,
        imageFormats: ["jpg", "jpeg", "gif", "png", "ico", "bmp", "webp"],
        imageUploadURL: uploadUrl,
        theme: dark ? "dark" : "default",
        previewTheme: dark ? "dark" : "default",
        editorTheme: dark ? "pastel-on-dark" : "default",

        onchange: function () {
            var content = mdEditor.getPreviewedHTML();
            $("#content").val(content);
            if (content === '') {
                $("#editormd").addClass("has-error");
                $("#editormd").css("border-color", "#a94442");
            } else {
                $("#editormd").removeClass("has-error");
                $("#editormd").css("border-color", "#ccc");
            }
        },
        onload: function () {
            $("#content").val(mdEditor.getPreviewedHTML());
            var keyMap = {
                "Ctrl-S": function () {
                    save(true, false);
                }
            };
            this.addKeyMap(keyMap);
            $("#fileDialog").on("click", function () {
                mdEditor.executePlugin("fileDialog", "../plugins/file-dialog/file-dialog");
            });
            $("#videoDialog").on("click", function () {
                mdEditor.executePlugin("videoDialog", "../plugins/video-dialog/video-dialog");
            });
            setInterval(function () {
                save(true, true);
            }, 1000 * 6);
        },
        onfullscreen: function () {
            $("#editormd").css("z-index", "9999");
            mdEditor.width("100%");
            if (screenfull.enabled) {
                screenfull.request();
            }
        },

        onfullscreenExit: function () {
            $("#editormd").css("z-index", 0);
            mdEditor.width("100%");
            if (screenfull.enabled) {
                screenfull.exit();
            }
        }

    });

    $(".editormd-markdown-textarea").attr("name", "mdContent");
    $(".editormd-html-textarea").removeAttr("name");

    function checkPreviewLink() {
        if ($("#id").val() === null || $("#id").val() === '') {
            $("#preview").attr("disable", "disable");
        } else {
            updatePreviewLink($("#id").val());
        }
    }

    checkPreviewLink();

    function updatePreviewLink(id) {
        $("#preview-link").attr("href", "admin/article/preview?id=" + id);
        $("#preview").removeClass("btn-");
        $("#preview-link").show();
    }

    function tips(data, message) {
        PNotify.removeAll();
        if (data.error === 0) {
            new PNotify({
                title: message,
                type: 'success',
                hide: true,
                delay: 3000,
                styling: 'bootstrap3'
            });
            $("#id").val(data.id);
            $("#alias").val(data.alias);
            $("#digest").val(data.digest);
            if (data.thumbnail !== null) {
                fillThumbnail(data.thumbnail);
            }
            updatePreviewLink(data.id);
            if (window.location.href.indexOf('?') === -1) {
                window.history.replaceState({}, "", window.location.pathname + "?id=" + data.id);
            }
        } else {
            new PNotify({
                title: data.message,
                type: 'error',
                hide: true,
                delay: 3000,
                styling: 'bootstrap3'
            });
        }
    }

    var saving = false;
    var lastChangeRequestBody;

    function validator(el) {
        if ($("#title").val() === '') {
            $("#title-parent").addClass("has-error");
        }
        if ($("#content").val() === '') {
            $("#editormd").addClass("has-error");
            $("#editormd").css("border-color", "#a94442");
        }
        return el.find(".has-error").length === 0;
    }

    $("#title").on("change keyup paste click", function () {
        if ($(this).val() !== '') {
            $("#title-parent").removeClass("has-error");
        } else {
            $("#title-parent").addClass("has-error");
        }
    });

    function save(rubbish, timer) {
        //如果是还在保存文章状态，跳过保存
        if (saving) {
            PNotify.removeAll();
            new PNotify({
                title: lang.saving,
                delay: 3000,
                type: 'warn',
                hide: true,
                styling: 'bootstrap3'
            });
            return;
        }
        refreshKeywords();
        var body = getFormRequestBody("#article-form");
        var tLastChangeRequestBody = JSON.stringify(body);
        var changed = tLastChangeRequestBody !== lastChangeRequestBody;
        if (validator($("#article-form")) && (!timer || changed)) {
            body['rubbish'] = rubbish;
            var url;
            if ($("#id").val() !== '') {
                url = "api/admin/article/update";
            } else {
                url = "api/admin/article/create";
            }
            saving = true;
            if (!skipFirstRubbishSave) {
                $.ajax({
                        url: url,
                        data: JSON.stringify(body),
                        method: "POST",
                        dataType: "json",
                        contentType: "application/json",
                        success: function (data) {
                            var date = new Date();
                            saving = false;
                            tips(data, (timer ? lang.auto : "") + (rubbish ? lang.rubbish : "") + " " + lang.saveSuccess + " " + zeroPad(date.getHours(), 2) + ":" + zeroPad(date.getMinutes(), 2) + ":" + zeroPad(date.getSeconds(), 2));
                            lastChangeRequestBody = JSON.stringify(getFormRequestBody("#article-form"));
                        },
                        error: function () {
                            saving = true;
                        }
                    }
                )
            } else {
                skipFirstRubbishSave = false;
                saving = false;
                lastChangeRequestBody = JSON.stringify(getFormRequestBody("#article-form"));
            }
        } else {
            lastChangeRequestBody = JSON.stringify(getFormRequestBody("#article-form"));
        }
    }

    $(document.body).on('click', '#unCheckedTag .tag2', function (e) {
        $("#keywords_tagsinput").find('span[val=' + $(this).text() + ']').remove();
        keywordsEl.importTags($(this).text());
        $(this).remove();
        e.preventDefault();
        refreshKeywords();
    });
    $(document.body).on('click', "#keywords_tagsinput .tag2 a", function () {
        var text = $(this).siblings().text().trim();
        $("#unCheckedTag").find('span[val=' + text + ']').remove();
        $(this).parent().remove();
        $("#unCheckedTag").append('' + text + '');
        refreshKeywords();
        return false;
    });

    function refreshKeywords() {
        var ts = $("#keywords_tagsinput .tag2").children("span");
        var keywordsVal = "";
        for (var i = 0; i < ts.length; i++) {
            keywordsVal = keywordsVal + $(ts[i]).text().trim() + ",";
        }
        $("#keywordsVal").val(keywordsVal);
    }

    $("#saveToRubbish").click(function () {
        skipFirstRubbishSave = false;
        save(true, false)
    });

    $("#save").click(function () {
        skipFirstRubbishSave = false;
        save(false, false);
    });

    $('#thumbnail-upload').liteUploader({
        script: 'api/admin/upload/thumbnail?dir=thumbnail'
    }).on('lu:success', function (e, response) {
        if (response.error) {
            alert(response.message);
        } else {
            fillThumbnail(response.url);
        }
    });

    function fillThumbnail(url) {
        $("#thumbnail-img").css('background-image', "url('" + url + "')");
        var w = gup("w", url);
        var h = gup("h", url);
        if (h) {
            if (w < 660) {
                h = 660.0 / w * h;
            }
            $("#thumbnail-img").height(h);
            $("#thumbnail").val(url);
            $("#camera-icon").hide();
        }
    }

    var thumbnailImg = $("input[name=\"thumbnail\"]").val();
    if (thumbnailImg !== "") {
        fillThumbnail(thumbnailImg);
    }

});

function gup(name, url) {
    if (!url) url = location.href;
    name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]");
    var regexS = "[\\?&]" + name + "=([^&#]*)";
    var regex = new RegExp(regexS);
    var results = regex.exec(url);
    return results === null ? null : results[1];
}<|endoftext|>


<commit_before>require.config({
	baseUrl : "./",
	waitSeconds: 15,
	paths : {
		"knockout" : "scripts/lib/knockout-3.1.0",
		"knockout-amd-helpers" : "scripts/lib/knockout-amd-helpers",
		"knockout-postbox" : "scripts/lib/knockout-postbox",
		"text" : "scripts/lib/text",
		"jquery" : "scripts/lib/jquery-1.9.1.min",
		"jquery-ui" : "scripts/lib/jquery-ui-1.10.3.custom.min",
		"highstock" : "scripts/lib/highstock"

	}
});

require([ "knockout","jquery", "scripts/modules/topicexplorer-view-model",
		"knockout-amd-helpers", "knockout-postbox",
		"text", "jquery-ui" ], function(ko, $, App) {
	var timeoutId = null;
	var self=this;
	ko.bindingHandlers.module.baseDir = "scripts/modules";

	// global delegates
	$(document).delegate(".menuActivator", 'click', function(e){
		if($(this).next().height() + 26 > $(this).parent().height()) {
	    	$(this).next().css('max-height', $(this).parent().height() - 26);
	    }

		$(this).toggleClass("rotate1 rotate2");
	    $(this).next().toggle('blind');

	}).delegate("#invisbleTabs > span", 'click', function() {
		$('#tabMenu').toggle('blind');
	}).delegate("a.ui-corner-all", 'mouseover', function() {
		$(this).addClass('ui-state-focus');
	}).delegate("a.ui-corner-all", 'mouseout', function() {
		$(this).removeClass('ui-state-focus');
	}).delegate(".tab", 'mouseover', function() {
		$(this).children('img').show();
	}).delegate(".tab", 'mouseout', function() {
		$(this).children('img').hide();
	}).delegate(".documentList li", 'mouseenter', function(){
		$(this).addClass('myHover').find(".docButtons").show();
	}).delegate(".documentList li", 'mouseleave', function(){
		$(this).removeClass('myHover').find(".docButtons").hide();
	}).delegate(".topicCircle, .topicCheckbox, .topicWord", "mouseover", function(){
		var self = this;
		if (!timeoutId) {
	        timeoutId = window.setTimeout(function() {
	            timeoutId = null;
	            moveToTopic($(self).attr('id').split('_')[1]);
		    }, 1500);
		}
	}).delegate(".topicCircle, .topicCheckbox, .topicWord", "mouseout", function(){
		if (timeoutId) {
		    window.clearTimeout(timeoutId);
		    timeoutId = null;
		}
	}).delegate("#groupG rect", "mouseover", function(){
		$(this).attr("height", "17");
		$(this).attr("y", "0");
	}).delegate("#groupG rect", "mouseout", function(){
		$(this).attr("height", "13");
		$(this).attr("y", "2");
	}).delegate(".topicList", "mouseenter", function(){
		$(this).children(":first").show();
	}).delegate("#desktop", "mouseenter", function(){
		$(this).children(":first").show();
		if($("#desktop").scrollTop() > 1000) {
			$("#jumpToStart").show();
		}
	}).delegate("#desktop, .topicList", "mouseleave", function(){
		$(this).children(":first").hide();
		$(this).children(":first").next().hide();
		$(this).children(":first").removeClass("rotate2");
		$(this).children(":first").addClass("rotate1");
		$("#jumpToStart").hide();
	}).delegate("#jumpToStart", "click", function() {
		$("#desktop").animate({
			scrollTop: 0
		});
	}).delegate(".circles .topicCircle, #groupG rect, .topicWord", "click", moveToTopic);

	$(document).tooltip();

	self.minHeight = 400;
	self.minWidth = 800;

	$(window).resize(function() {
		ko.postbox.publish("windowWidth",Math.max(self.minWidth, $(window).width(), /* For opera: */ document.documentElement.clientWidth));
		ko.postbox.publish("windowHeight",Math.max(self.minHeight, $(window).height(), /* For opera: */ document.documentElement.clientHeight));
	});

	setTimeout(function() {
		ko.applyBindings(new App());
	}, 0);

	setTimeout(function() {
		$(window).trigger('resize');
	}, 3000);

});

function makeMenu(el) {
	$(el).menu({
		select : function(event, ui) {
			$(this).parent().parent().prev()
				.toggleClass("rotate1 rotate2");
			$(this).parent().parent().toggle('blind');
		}
	});
};

function getScrollPositionByValue(val) {
	var offset = 3;
	var scroll = val;
	var maxScrollPos = $('.topicBottomSliderDiv').width() - $('.topicPrevSlider').width();
	var maxScroll = $('.topicList > ul').width() - $('.topicList').width();
	var position = Math.round(scroll*maxScrollPos/maxScroll)+offset;
	if(position < 0)
		position = 0;
	if(position > maxScrollPos)
		position = maxScrollPos;
	return position;
};

function moveToTopic(self) {

	var topic_id = 0;
	if(!self.currentTarget) {
		topic_id = self;
	} else {
		topic_id = $(self.currentTarget).attr('id').split("_")[1];
	}
	var offset = $(".topicList").width() / 2 - $("#topic" + topic_id).width() / 2;

	$(".topicList").animate({
		scrollLeft : ($("#topic" + topic_id).position().left - offset)
	}, {
		duration : 2000,
		easing : "swing"
	});
	$(".topicPrevSlider").animate({
		left : getScrollPositionByValue(($("#topic" + topic_id).position().left - offset))
	}, {
		duration : 2000,
		easing : "swing"
	});
	setTimeout(function() {
		$(".topicList > img, #topicMenu").css('left', $(".topicList").scrollLeft());
	}, 2000);
};

<commit_msg>#57 remove hover events for  clickable topic circles<commit_after>require.config({
	baseUrl : "./",
	waitSeconds: 15,
	paths : {
		"knockout" : "scripts/lib/knockout-3.1.0",
		"knockout-amd-helpers" : "scripts/lib/knockout-amd-helpers",
		"knockout-postbox" : "scripts/lib/knockout-postbox",
		"text" : "scripts/lib/text",
		"jquery" : "scripts/lib/jquery-1.9.1.min",
		"jquery-ui" : "scripts/lib/jquery-ui-1.10.3.custom.min",
		"highstock" : "scripts/lib/highstock"

	}
});

require([ "knockout","jquery", "scripts/modules/topicexplorer-view-model",
		"knockout-amd-helpers", "knockout-postbox",
		"text", "jquery-ui" ], function(ko, $, App) {
	var timeoutId = null;
	var self=this;
	ko.bindingHandlers.module.baseDir = "scripts/modules";

	// global delegates
	$(document).delegate(".menuActivator", 'click', function(e){
		if($(this).next().height() + 26 > $(this).parent().height()) {
	    	$(this).next().css('max-height', $(this).parent().height() - 26);
	    }

		$(this).toggleClass("rotate1 rotate2");
	    $(this).next().toggle('blind');

	}).delegate("#invisbleTabs > span", 'click', function() {
		$('#tabMenu').toggle('blind');
	}).delegate("a.ui-corner-all", 'mouseover', function() {
		$(this).addClass('ui-state-focus');
	}).delegate("a.ui-corner-all", 'mouseout', function() {
		$(this).removeClass('ui-state-focus');
	}).delegate(".tab", 'mouseover', function() {
		$(this).children('img').show();
	}).delegate(".tab", 'mouseout', function() {
		$(this).children('img').hide();
	}).delegate(".documentList li", 'mouseenter', function(){
		$(this).addClass('myHover').find(".docButtons").show();
	}).delegate(".documentList li", 'mouseleave', function(){
		$(this).removeClass('myHover').find(".docButtons").hide();
	}).delegate(".ui-autocomplete .topicCircle, .topicCheckbox", "mouseover", function(){
		var self = this;
		if (!timeoutId) {
	        timeoutId = window.setTimeout(function() {
	            timeoutId = null;
	            moveToTopic($(self).attr('id').split('_')[1]);
		    }, 1500);
		}
	}).delegate(".ui-autocomplete .topicCircle, .topicCheckbox", "mouseout", function(){
		if (timeoutId) {
		    window.clearTimeout(timeoutId);
		    timeoutId = null;
		}
	}).delegate("#groupG rect", "mouseover", function(){
		$(this).attr("height", "17");
		$(this).attr("y", "0");
	}).delegate("#groupG rect", "mouseout", function(){
		$(this).attr("height", "13");
		$(this).attr("y", "2");
	}).delegate(".topicList", "mouseenter", function(){
		$(this).children(":first").show();
	}).delegate("#desktop", "mouseenter", function(){
		$(this).children(":first").show();
		if($("#desktop").scrollTop() > 1000) {
			$("#jumpToStart").show();
		}
	}).delegate("#desktop, .topicList", "mouseleave", function(){
		$(this).children(":first").hide();
		$(this).children(":first").next().hide();
		$(this).children(":first").removeClass("rotate2");
		$(this).children(":first").addClass("rotate1");
		$("#jumpToStart").hide();
	}).delegate("#jumpToStart", "click", function() {
		$("#desktop").animate({
			scrollTop: 0
		});
	}).delegate(".circles .topicCircle, #groupG rect, .topicWord", "click", moveToTopic);

	$(document).tooltip();

	self.minHeight = 400;
	self.minWidth = 800;

	$(window).resize(function() {
		ko.postbox.publish("windowWidth",Math.max(self.minWidth, $(window).width(), /* For opera: */ document.documentElement.clientWidth));
		ko.postbox.publish("windowHeight",Math.max(self.minHeight, $(window).height(), /* For opera: */ document.documentElement.clientHeight));
	});

	setTimeout(function() {
		ko.applyBindings(new App());
	}, 0);

	setTimeout(function() {
		$(window).trigger('resize');
	}, 3000);

});

function makeMenu(el) {
	$(el).menu({
		select : function(event, ui) {
			$(this).parent().parent().prev()
				.toggleClass("rotate1 rotate2");
			$(this).parent().parent().toggle('blind');
		}
	});
};

function getScrollPositionByValue(val) {
	var offset = 3;
	var scroll = val;
	var maxScrollPos = $('.topicBottomSliderDiv').width() - $('.topicPrevSlider').width();
	var maxScroll = $('.topicList > ul').width() - $('.topicList').width();
	var position = Math.round(scroll*maxScrollPos/maxScroll)+offset;
	if(position < 0)
		position = 0;
	if(position > maxScrollPos)
		position = maxScrollPos;
	return position;
};

function moveToTopic(self) {

	var topic_id = 0;
	if(!self.currentTarget) {
		topic_id = self;
	} else {
		topic_id = $(self.currentTarget).attr('id').split("_")[1];
	}
	var offset = $(".topicList").width() / 2 - $("#topic" + topic_id).width() / 2;

	$(".topicList").animate({
		scrollLeft : ($("#topic" + topic_id).position().left - offset)
	}, {
		duration : 2000,
		easing : "swing"
	});
	$(".topicPrevSlider").animate({
		left : getScrollPositionByValue(($("#topic" + topic_id).position().left - offset))
	}, {
		duration : 2000,
		easing : "swing"
	});
	setTimeout(function() {
		$(".topicList > img, #topicMenu").css('left', $(".topicList").scrollLeft());
	}, 2000);
};

<|endoftext|>


<commit_before>/*
 * Copyright 2013 Jive Software
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

/**
 * This is the main entry point of the api.
 */

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// persistence
exports.persistence = {
    'file' : require('./persistence/file'),
    'memory' : require('./persistence/memory'),
    'mongo' : require('./persistence/mongo')
};

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// configuration
exports.config = require('./lib/config');

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// external streams and tiles

var eventRegistry = require( './tile/registry');

var extstreamsDefinitions = require('./lib/extstreamsDefinitions');
var extstreams = new (require('./lib/extstreams'));
extstreams['definitions'] = new extstreamsDefinitions();
extstreams['events'] = eventRegistry;
exports.extstreams = extstreams;

var tileDefinitions = require('./lib/tilesDefinitions');
var tiles = new (require('./lib/tiles'));
tiles['definitions'] = new tileDefinitions();
tiles['events'] = eventRegistry;
exports.tiles = tiles;

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// events
exports.events = eventRegistry;

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// tasks
exports.tasks = require('./lib/tasks');

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// util
exports.util = require('./lib/jiveutil');

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// autowire
exports.autowire = require('./autowire/api');

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// routes
exports.routes = require('./routes/tiles');

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// applications xxx todo -- this might not be necessary in the api
exports.applications = new (require('./lib/applications'));
<commit_msg>Cleanup<commit_after>/*
 * Copyright 2013 Jive Software
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

/**
 * This is the main entry point of the api.
 */

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// persistence
exports.persistence = {
    'file' : require('./persistence/file'),
    'memory' : require('./persistence/memory'),
    'mongo' : require('./persistence/mongo')
};

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// configuration
exports.config = require('./lib/config');

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// external streams and tiles (instances and definitions)
// eg. jive.tiles
//     jive.tiles.definitions
//     jive.extstreams
//     jive.extstreams.definitions

var eventRegistry = require( './tile/registry');

var extstreamsDefinitions = require('./lib/extstreamsDefinitions');
var extstreams = new (require('./lib/extstreams'));
extstreams['definitions'] = new extstreamsDefinitions();
extstreams['events'] = eventRegistry;
exports.extstreams = extstreams;

var tileDefinitions = require('./lib/tilesDefinitions');
var tiles = new (require('./lib/tiles'));
tiles['definitions'] = new tileDefinitions();
tiles['events'] = eventRegistry;
exports.tiles = tiles;

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// events
exports.events = eventRegistry;

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// tasks
exports.tasks = require('./lib/tasks');

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// util
exports.util = require('./lib/jiveutil');

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// autowire
exports.autowire = require('./autowire/api');

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// routes
exports.routes = require('./routes/tiles');

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// applications xxx todo -- this might not be necessary in the api
exports.applications = new (require('./lib/applications'));
<|endoftext|>


<commit_before>'use strict';

$.ajaxSetup({
  xhrFields: {
    withCredentials: true
  }
});

var api = {

  url: 'http://localhost:3000',

  ajax: function(config, cb) {
  $.ajax(config).done(function(data, textStatus, jqxhr) {
      cb(null, data);
    }).fail(function(jqxhr, status, error) {
      cb({jqxher: jqxhr, status: status, error: error});
    });
  },

  // Unauthenticated actions
  register: function(credentials, callback) {
    this.ajax({
      method: 'POST',
      url: this.url + '/signup',
      contentType: 'application/json',
      data: JSON.stringify(credentials)
      //dataType: 'json'
    }, callback);
  },

  login: function(credentials, callback) {
    this.ajax({
      method: 'POST',
      url: this.url + '/login',
      contentType: 'application/json',
      data: JSON.stringify(credentials)
      //dataType: 'json'
    }, callback);
  },

  logout: function(callback) {
    this.ajax({
      method: 'POST',
      url: this.url + '/logout',
      xhrFields: {
                  withCredentials: true
                  }
      // contentType: 'application/json'
      //dataType: 'json'
    }, callback);
  },

  indexProducts: function(callback) {
    this.ajax({
      method: 'GET',
      url: this.url + '/products',
      contentType: 'application/json'
      //dataType: 'json'
    }, callback);
  },

  showProduct: function(id, callback) {
    this.ajax({
      method: 'GET',
      url: this.url + '/products/' + id,
      contentType: 'application/json'
      //dataType: 'json'
    }, callback);
  },

  showTransaction: function(query, callback) {
    this.ajax({
      method: 'GET',
      url: this.url + '/transactions/' + query,
      contentType: 'application/json'
      //dataType: 'json'
    }, callback);
  },

  createTransaction: function(data, callback) {
    this.ajax({
      method: 'POST',
      url: this.url + '/transactions',
      contentType: 'application/json'
      //dataType: 'json'
    }, callback);
  },

  updateTransaction: function(id, data, callback) {
    this.ajax({
      method: 'PATCH',
      url: this.url + '/transactions/' + id,
      contentType: 'application/json',
      data: data
      //dataType: 'json'
    }, callback);
  }

};
<commit_msg>Adds xhrFields ajax setting. -mk<commit_after>'use strict';

var api = {

  url: 'http://localhost:3000',

  ajax: function(config, cb) {
    $.ajaxSetup({
      xhrFields: {
        withCredentials: true
      }
    });
    $.ajax(config).done(function(data, textStatus, jqxhr) {
        cb(null, data);
      }).fail(function(jqxhr, status, error) {
        cb({jqxher: jqxhr, status: status, error: error});
      });
    },

  // Unauthenticated actions
  register: function(credentials, callback) {
    this.ajax({
      method: 'POST',
      url: this.url + '/signup',
      contentType: 'application/json',
      data: JSON.stringify(credentials)
      //dataType: 'json'
    }, callback);
  },

  login: function(credentials, callback) {
    this.ajax({
      method: 'POST',
      url: this.url + '/login',
      contentType: 'application/json',
      data: JSON.stringify(credentials)
      //dataType: 'json'
    }, callback);
  },

  logout: function(callback) {
    this.ajax({
      method: 'POST',
      url: this.url + '/logout',
      contentType: 'application/json'
      //dataType: 'json'
    }, callback);
  },

  indexProducts: function(callback) {
    this.ajax({
      method: 'GET',
      url: this.url + '/products',
      contentType: 'application/json'
      //dataType: 'json'
    }, callback);
  },

  showProduct: function(id, callback) {
    this.ajax({
      method: 'GET',
      url: this.url + '/products/' + id,
      contentType: 'application/json'
      //dataType: 'json'
    }, callback);
  },

  showTransaction: function(query, callback) {
    this.ajax({
      method: 'GET',
      url: this.url + '/transactions/' + query,
      contentType: 'application/json'
      //dataType: 'json'
    }, callback);
  },

  createTransaction: function(data, callback) {
    this.ajax({
      method: 'POST',
      url: this.url + '/transactions',
      contentType: 'application/json'
      //dataType: 'json'
    }, callback);
  },

  updateTransaction: function(id, data, callback) {
    this.ajax({
      method: 'PATCH',
      url: this.url + '/transactions/' + id,
      contentType: 'application/json',
      data: data
      //dataType: 'json'
    }, callback);
  }

};
<|endoftext|>


<commit_before>function Api(config) {
	this.requests = {};
	this.opts = { baseUrl: config.baseUrl, apiPrefix: config.apiPath, headers: config.defaultHeaders, post: config.post };
}

Api.prototype = {
	/**
	 * Make an API request without path api path prefixing
	 *
	 * @param {string} path Full path for request
	 * @param {object} data Key-value pairs to send to the server
	 * @param {ApiRequestOptions} opts API request options
	 * @return {Promise} Promise response for get
	 */
	raw: function(path, data, opts) {
		return new Promise(function(resolve, reject) {
			var req = new ApiRequest({path: path})
					   .using({method: (((typeof data == 'undefined') || data === null) ? 'GET' : 'POST')})
					   .using(this.opts)
					   .using(opts);

			httpRequest = new XMLHttpRequest();

			httpRequest.onload = function() {
				if (this.status >= 200 && this.status <= 299)
					resolve(this.response);
				else
					reject(this.statusText);
			},

			httpRequest.onerror = function() {
				console.log("ERR");
				reject(this.statusText);
			},

			data = data || null;
			if (typeof data == 'object')
				data = ApiRequest.serializePostData(data);
			if (data && req.method == 'GET')
				req.addQueryParams(data);

			httpRequest.open(req.method, req.buildUrl(), true);

			for (var name in req.headers)
				httpRequest.setRequestHeader(name, req.headers[name]);

			if (data && req.method != 'GET')
				httpRequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded");

			try {
				var retval = httpRequest.send(data);
			} catch (e) {
				reject(data);
			}
		}.bind(this));
	},

	/**
	 * Add default headers for API rquests
	 *
	 * @param {object} headers Key-value pairs of headers
	 * @return this
	 */
	addHeaders: function(headers) {
		this.opts.headers = Object.assign(this.opts.headers, headers);
		return this
	},

	/**
	 * Make an API GET request
	 *
	 * @param {string} path Relative path for request
	 * @param {object} data Key-value pairs for the query string
	 * @return {Promise} Promise response for get
	 */
	get: function(path, data) {
		return this.req(path, data, { method: 'GET' });
	},

	/**
	 * Make an API request
	 *
	 * @param {string} path Relative path for request
	 * @param {object} data Key-value pairs to send to the server
	 * @param {ApiRequestOptions} opts API request options
	 * @return {Promise} Promise response for get
	 */
	req: function(path, data, opts) {
		return this.raw(((opts || {}).apiPrefix || this.opts.apiPrefix) + path, data, opts)
			.then(function(response) {
				return JSON.parse(response);
			}.bind(this));
	},

	/**
	 * Make a single API request to an endpoint.  If there is currently a request inflight, return that request
	 *
	 * @param {string} path Relative path for request
	 * @param {object} data Key-value pairs to send to the server
	 * @param {ApiRequestOptions} opts API request options
	 * @return {Promise} Promise response for get
	 */
	once: function(path, data, opts) {
		if (!this.requests[path]) {
			this.requests[path] = this.req(path, data, opts)
				.then(function(results) {
					if (this.requests[path])
						delete this.requests[path];
					return results;
				}.bind(this));
		}
		return this.requests[path];
	}

}
<commit_msg>Reject inflight connections<commit_after>function Api(config) {
	this.requests = {};
	this.opts = { baseUrl: config.baseUrl, apiPrefix: config.apiPath, headers: config.defaultHeaders, post: config.post };
}

Api.prototype = {
	/**
	 * Make an API request without path api path prefixing
	 *
	 * @param {string} path Full path for request
	 * @param {object} data Key-value pairs to send to the server
	 * @param {ApiRequestOptions} opts API request options
	 * @return {Promise} Promise response for get
	 */
	raw: function(path, data, opts) {
		return new Promise(function(resolve, reject) {
			var req = new ApiRequest({path: path})
					   .using({method: (((typeof data == 'undefined') || data === null) ? 'GET' : 'POST')})
					   .using(this.opts)
					   .using(opts);

			httpRequest = new XMLHttpRequest();

			httpRequest.onload = function() {
				if (this.status >= 200 && this.status <= 299)
					resolve(this.response);
				else
					reject(this.statusText);
			},

			httpRequest.onerror = function() {
				console.log("ERR");
				reject(this.statusText);
			},

			data = data || null;
			if (typeof data == 'object')
				data = ApiRequest.serializePostData(data);
			if (data && req.method == 'GET')
				req.addQueryParams(data);

			httpRequest.open(req.method, req.buildUrl(), true);

			for (var name in req.headers)
				httpRequest.setRequestHeader(name, req.headers[name]);

			if (data && req.method != 'GET')
				httpRequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded");

			try {
				var retval = httpRequest.send(data);
			} catch (e) {
				reject(data);
			}
		}.bind(this));
	},

	/**
	 * Add default headers for API rquests
	 *
	 * @param {object} headers Key-value pairs of headers
	 * @return this
	 */
	addHeaders: function(headers) {
		this.opts.headers = Object.assign(this.opts.headers, headers);
		return this
	},

	/**
	 * Make an API GET request
	 *
	 * @param {string} path Relative path for request
	 * @param {object} data Key-value pairs for the query string
	 * @return {Promise} Promise response for get
	 */
	get: function(path, data) {
		return this.req(path, data, { method: 'GET' });
	},

	/**
	 * Make an API request
	 *
	 * @param {string} path Relative path for request
	 * @param {object} data Key-value pairs to send to the server
	 * @param {ApiRequestOptions} opts API request options
	 * @return {Promise} Promise response for get
	 */
	req: function(path, data, opts) {
		return this.raw(((opts || {}).apiPrefix || this.opts.apiPrefix) + path, data, opts)
			.then(function(response) {
				return JSON.parse(response);
			}.bind(this));
	},

	/**
	 * Make a single API request to an endpoint.  If there is currently a request inflight, return that request
	 *
	 * @param {string} path Relative path for request
	 * @param {object} data Key-value pairs to send to the server
	 * @param {ApiRequestOptions} opts API request options
	 * @return {Promise} Promise response for get
	 */
	once: function(path, data, opts) {
		if (this.requests[path]) {
			console.log('INFLIGHT', path);
			return Promise.reject({});
		}
		if (!this.requests[path]) {
			this.requests[path] = this.req(path, data, opts)
				.then(function(results) {
					if (this.requests[path])
						delete this.requests[path];
					return results;
				}.bind(this), function(results) {
					if (this.requests[path])
						delete this.requests[path];
					return results;
				}.bind(this));
		}
		return this.requests[path];
	}

}
<|endoftext|>


<commit_before>/*

Selectors

 q			Query
 qi			Query by Id
 qa			Query All
 qs			Query in Sub element
 qas		Query All in Sub element

For each

 ff			For each call Function
 qaff		Query All For each call Function
 qasff		Query All in Sub element For each call Function
 fif		For each Integer from 0 call Function

Requests

 r			Request
 rp			Request Post
 rg			Request Get
 rgr		Request Get Raw link

Other

 cloneTemplate
 elementFromHtml
 onEnter
 onScrollNearEnd

*/

// Selectors

function qi(id) {
	return document.getElementById(id);
}

function q(text) {
	return document.querySelector(text);
}

function qa(text) {
	return document.querySelectorAll(text);
}

function qs(sub, text) {
	return sub.querySelector(text);
}

function qas(sub, text) {
	return sub.querySelectorAll(text);
}

// For each

function ff(l, f) {
	var count = l.length;
	for(var i=0; i<count; i+=1) f(l[i]);
}

function qaff(text, f) {
	ff(qa(text), f);
}

function qas(sub, text, f) {
	ff(qas(sub, text), f);
}

function fif(n, f) {
	for(var i=0; i<n; i+=1) f(i);
}

// Requests

function r(method, link, params, callback, callback_error) {

	var req = new XMLHttpRequest();

	req.onreadystatechange = function() {
		if(req.readyState == 4) {
			if (req.status >= 200 && req.status < 300) {
				callback(req.responseText, req.status);
			}else{
				callback_error(req.responseText, req.status);
			}
		}
	};

	var couples = [];
	for(var i in params) {
		if(params[i]!="") couples.push(encodeURIComponent(i) + "=" + encodeURIComponent(params[i]));
		else couples.push(encodeURIComponent(i));
	}
	var paramstext = couples.join("&");

	if(method == "POST") {
		req.open(method, link, true);
		req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
		req.send(paramstext);
	}else{
		if(paramstext!="") link += "?" + paramstext;
		req.open(method, link, true);
		req.send();
	}
}

function rp(link, params, callback, callback_error) {
	r("POST", link, params, callback, callback_error);
}

function rg(link, params, callback, callback_error) {
	r("GET", link, params, callback, callback_error);
}

function rgr(link, callback, callback_error) {
	r("GET", link, {}, callback, callback_error);
}

// Other

function cloneTemplate(id) {
	return qi(id).content.children[0].cloneNode(true);
}

function elementFromHtml(text) {
	var tmp = document.createElement("div");
	tmp.innerHTML = text;
	return tmp.children[0];
}

function onEnter(ele, f) {
	ele.addEventListener("keydown", function (e) {
		if (e.keyCode === 13) {
			f();
			e.preventDefault();
		}
	});
}

function onScrollNearEnd(limit, f) {
	window.onscroll = function() {
		if(window.innerHeight + window.scrollY >= document.body.offsetHeight - limit) {
			f();
		}
	}
}
<commit_msg>Format style fixes<commit_after>/*

Selectors

 q			Query
 qi			Query by Id
 qa			Query All
 qs			Query in Sub element
 qas		Query All in Sub element

For each

 ff			For each call Function
 qaff		Query All For each call Function
 qasff		Query All in Sub element For each call Function
 fif		For each Integer from 0 call Function

Requests

 r			Request
 rp			Request Post
 rg			Request Get
 rgr		Request Get Raw link

Other

 cloneTemplate
 elementFromHtml
 onEnter
 onScrollNearEnd

*/

// Selectors

function qi(id) {
	return document.getElementById(id);
}

function q(text) {
	return document.querySelector(text);
}

function qa(text) {
	return document.querySelectorAll(text);
}

function qs(sub, text) {
	return sub.querySelector(text);
}

function qas(sub, text) {
	return sub.querySelectorAll(text);
}

// For each

function ff(l, f) {
	var count = l.length;
	for(var i = 0; i < count; i += 1) f(l[i]);
}

function qaff(text, f) {
	ff(qa(text), f);
}

function qas(sub, text, f) {
	ff(qas(sub, text), f);
}

function fif(n, f) {
	for(var i = 0; i < n; i += 1) f(i);
}

// Requests

function r(method, link, params, callback, callback_error) {

	var req = new XMLHttpRequest();

	req.onreadystatechange = function() {
		if(req.readyState == 4) {
			if (req.status >= 200 && req.status < 300) {
				callback(req.responseText, req.status);
			} else {
				callback_error(req.responseText, req.status);
			}
		}
	};

	var couples = [];
	for(var i in params) {
		if(params[i] != "") couples.push(encodeURIComponent(i) + "=" + encodeURIComponent(params[i]));
		else couples.push(encodeURIComponent(i));
	}
	var paramstext = couples.join("&");

	if(method == "POST") {
		req.open(method, link, true);
		req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
		req.send(paramstext);
	}else{
		if(paramstext != "") link += "?" + paramstext;
		req.open(method, link, true);
		req.send();
	}
}

function rp(link, params, callback, callback_error) {
	r("POST", link, params, callback, callback_error);
}

function rg(link, params, callback, callback_error) {
	r("GET", link, params, callback, callback_error);
}

function rgr(link, callback, callback_error) {
	r("GET", link, {}, callback, callback_error);
}

// Other

function cloneTemplate(id) {
	return qi(id).content.children[0].cloneNode(true);
}

function elementFromHtml(text) {
	var tmp = document.createElement("div");
	tmp.innerHTML = text;
	return tmp.children[0];
}

function onEnter(ele, f) {
	ele.addEventListener("keydown", function (e) {
		if (e.keyCode === 13) {
			f();
			e.preventDefault();
		}
	});
}

function onScrollNearEnd(limit, f) {
	window.onscroll = function() {
		if(window.innerHeight + window.scrollY >= document.body.offsetHeight - limit) {
			f();
		}
	}
}
<|endoftext|>


<commit_before><commit_msg>erm stuff<commit_after><|endoftext|>


<commit_before>// The erlang.js API
//

module.exports = { "term_to_binary": require('./term_to_binary').term_to_binary
                 }
<commit_msg>optlist_to_binary in the public API<commit_after>// The erlang.js API
//

var term_to_binary = require('./term_to_binary');

module.exports = { "term_to_binary": term_to_binary.term_to_binary
                 , "optlist_to_binary": term_to_binary.optlist_to_binary
                 }
<|endoftext|>