<commit_before>#!/usr/bin/env node --harmony
'use strict';

/**
 * Dependencies
 */
var argv = require('optimist').argv
  , _ = require('lodash')
  , path = require('path')
  , fs = require('fs-extra');

// Stringify args
argv._ = _.map(argv._, function (arg) {
  return arg + '';
});

// Start this app
if (argv._[0] && _.contains(['start', 'server', 's'], argv._[0])) {
  require(path.join(process.cwd(), 'index'));
}

// Basic usage
else if (argv._.length === 0) {
  console.log('');
  console.log('Welcome to Koan!');
  console.log('');
  displayUsage();
}

// Create a new app
// second argument == app name
else if (argv._[0].match(/^new$/)) {

  verifyArg(1, "Please specify the name of the new project folder: e.g.\n koan new <appName>");

  var destination = path.join(process.cwd(), argv._[1]);

  fs.copy(path.join(__dirname, '../lib/boilerplates/application'), destination,  function(err) {
    if (err) {
      console.error(err);
      process.exit(1);
    }

    console.info('New Koan application is created.');
  });
}

// Unknown command, print out usage
else {
  console.log('');
  displayUsage();
  console.log.error(argv._[0] + ' is not a valid action.');
}

// Display usage
function displayUsage() {
  var usage = 'Usage: koan <command>\n\n';
  usage += 'koan start: Run the app in the current dir\n';
  usage += 'koan new <appName>: Create a new Koan project in a folder called <appName>\n';

  console.info(usage);
}

// Verify that an argument exists
function verifyArg(argNo, msg) {
  if (!argv._[argNo]) {
    console.error(msg);
    process.exit(1);
  }
}
<commit_msg>Fixing Travis build.<commit_after>#!/usr/bin/env node
'use strict';

/**
 * Dependencies
 */
var argv = require('optimist').argv
  , _ = require('lodash')
  , path = require('path')
  , fs = require('fs-extra')
  , fork = require('child_process').fork;

// Stringify args
argv._ = _.map(argv._, function (arg) {
  return arg + '';
});

// Start this app
if (argv._[0] && _.contains(['start', 'server', 's'], argv._[0])) {
  fork(path.join(process.cwd(), 'index'), [], {execArgv: ['--harmony']});
}

// Basic usage
else if (argv._.length === 0) {
  console.log('');
  console.log('Welcome to Koan!');
  console.log('');
  displayUsage();
}

// Create a new app
// second argument == app name
else if (argv._[0].match(/^new$/)) {

  verifyArg(1, "Please specify the name of the new project folder: e.g.\n koan new <appName>");

  var destination = path.join(process.cwd(), argv._[1]);

  fs.copy(path.join(__dirname, '../lib/boilerplates/application'), destination,  function(err) {
    if (err) {
      console.error(err);
      process.exit(1);
    }

    console.info('New Koan application is created.');
  });
}

// Unknown command, print out usage
else {
  console.log('');
  displayUsage();
  console.log.error(argv._[0] + ' is not a valid action.');
}

// Display usage
function displayUsage() {
  var usage = 'Usage: koan <command>\n\n';
  usage += 'koan start: Run the app in the current dir\n';
  usage += 'koan new <appName>: Create a new Koan project in a folder called <appName>\n';

  console.info(usage);
}

// Verify that an argument exists
function verifyArg(argNo, msg) {
  if (!argv._[argNo]) {
    console.error(msg);
    process.exit(1);
  }
}
<|endoftext|>


<commit_before>var debug = {
    MapTextTest: function(skip) {
        var allText = [];
        var doSkip = (skip !== undefined);
        for(var i in fulltext) {
            if(fulltext[i].type !== "map") { continue; }
            if(doSkip) { if(i === skip) { doSkip = false; } else { continue; } }
            allText.push(function(x) { return function() { console.log(x); worldmap.writeText(x); }; }(i));
        }
        mapentities["farm"].push({ name: "DebugFriend", pos: { x: 0, y: 0 }, solid: false, autoplay: true, interact: allText });
        game.innerTransition(game.currentInputHandler, worldmap, { init: { x: 1, y: 1 }, map: "farm" });
    }
};
worldmap.title = {
    cursory: 0, showContinue: false,
    layersToClear: ["menutext", "menucursorA"],
    menuItems: [],
    setup: function(cursy) {
        this.cursory = cursy || 0;
        this.showContinue = this.hasSaves();
        gfx.drawFullImage("title");
        this.menuItems = this.getMainMenuItems();
        this.drawMenu();
    },
    getMainMenuItems: function() {
        return (this.hasSaves() ? ["New Game", "Continue", "Options"] : ["New Game", "Options"]);
    },
    hasSaves: function() {
        for(var i = 0; i < 10; i++) {
            if(localStorage.getItem("file" + i) !== null) { return true; }
        }
        return false;
    },
    drawMenu: function() {
        gfx.clearSome(this.layersToClear);
        for(var i = 0; i < this.menuItems.length; i++) {
            var selected = this.cursory === i;
            var spr = (selected ? "titleSelActive" : "titleSel");
            if(selected) { gfx.drawTileToGrid("carrotSel", 5, i + 6, "menutext"); }
            gfx.drawTileToGrid(spr + "0", 6, i + 6, "menutext");
            gfx.drawTileToGrid(spr + "1", 7, i + 6, "menutext");
            gfx.drawTileToGrid(spr + "2", 8, i + 6, "menutext");
            var dx = (176 - gfx.getTextLength(this.menuItems[i])) / 8;
            gfx.drawText(this.menuItems[i], 99.5 + dx, i * 16 + 106);
        }
    },
    mouseMove: function(pos) {
        if(pos.y < 0 || pos.y >= this.menuItems.length) { return false; }
        this.cursory = pos.y;
        this.drawMenu();
        return true;
    },
    click: function(pos) {
        switch(this.cursory) {
            case 0:
                return game.transition(this, worldmap, {
                    //init: { x: 10,  y: 5 }, map: "farmersmarket"
                    init: { x: 17,  y: 9 }, map: "farm_init"
                });
            case 1: return game.innerTransition(this, pausemenu.savemenu, { saving: false }); // this is probably wrong
            case 2: return game.innerTransition(this, worldmap.optionsMenu);
        }
    },
    clean: function() { gfx.clearAll(); },
    keyPress: function(key) {
        var pos = { x: 0, y: this.cursory };
        var isEnter = false;
        switch(key) {
            case player.controls.up: pos.y--; break;
            case player.controls.down: pos.y++; break;
            case player.controls.confirm:
            case player.controls.pause: isEnter = true; break;
            //case player.controls.cancel: return this.cancel();
        }
        if(pos.y < 0 || pos.y > 2) { return false; }
        if(isEnter) {
            return this.click(pos);
        } else {
            return this.mouseMove(pos);
        }
    }
};<commit_msg>more debug functions.<commit_after>var debug = {
    MapTextTest: function(skip) {
        var allText = [];
        var doSkip = (skip !== undefined);
        for(var i in fulltext) {
            if(fulltext[i].type !== "map") { continue; }
            if(doSkip) { if(i === skip) { doSkip = false; } else { continue; } }
            allText.push(function(x) { return function() { console.log(x); worldmap.writeText(x); }; }(i));
        }
        mapentities["farm"].push({ name: "DebugFriend", pos: { x: 0, y: 0 }, solid: false, autoplay: true, interact: allText });
        game.innerTransition(game.currentInputHandler, worldmap, { init: { x: 1, y: 1 }, map: "farm" });
    },
    QuickTest: function() { worldmap.noClip = true; me.PLAYERMOVESPEED = 0.5; },
    UndoQuickTest: function() { worldmap.noClip = false; me.PLAYERMOVESPEED = 0.25; }
};
worldmap.title = {
    cursory: 0, showContinue: false,
    layersToClear: ["menutext", "menucursorA"],
    menuItems: [],
    setup: function(cursy) {
        this.cursory = cursy || 0;
        this.showContinue = this.hasSaves();
        gfx.drawFullImage("title");
        this.menuItems = this.getMainMenuItems();
        this.drawMenu();
    },
    getMainMenuItems: function() {
        return (this.hasSaves() ? ["New Game", "Continue", "Options"] : ["New Game", "Options"]);
    },
    hasSaves: function() {
        for(var i = 0; i < 10; i++) {
            if(localStorage.getItem("file" + i) !== null) { return true; }
        }
        return false;
    },
    drawMenu: function() {
        gfx.clearSome(this.layersToClear);
        for(var i = 0; i < this.menuItems.length; i++) {
            var selected = this.cursory === i;
            var spr = (selected ? "titleSelActive" : "titleSel");
            if(selected) { gfx.drawTileToGrid("carrotSel", 5, i + 6, "menutext"); }
            gfx.drawTileToGrid(spr + "0", 6, i + 6, "menutext");
            gfx.drawTileToGrid(spr + "1", 7, i + 6, "menutext");
            gfx.drawTileToGrid(spr + "2", 8, i + 6, "menutext");
            var dx = (176 - gfx.getTextLength(this.menuItems[i])) / 8;
            gfx.drawText(this.menuItems[i], 99.5 + dx, i * 16 + 106);
        }
    },
    mouseMove: function(pos) {
        if(pos.y < 0 || pos.y >= this.menuItems.length) { return false; }
        this.cursory = pos.y;
        this.drawMenu();
        return true;
    },
    click: function(pos) {
        switch(this.cursory) {
            case 0:
                return game.transition(this, worldmap, {
                    //init: { x: 10,  y: 5 }, map: "farmersmarket"
                    init: { x: 17,  y: 9 }, map: "farm_init"
                });
            case 1: return game.innerTransition(this, pausemenu.savemenu, { saving: false }); // this is probably wrong
            case 2: return game.innerTransition(this, worldmap.optionsMenu);
        }
    },
    clean: function() { gfx.clearAll(); },
    keyPress: function(key) {
        var pos = { x: 0, y: this.cursory };
        var isEnter = false;
        switch(key) {
            case player.controls.up: pos.y--; break;
            case player.controls.down: pos.y++; break;
            case player.controls.confirm:
            case player.controls.pause: isEnter = true; break;
            //case player.controls.cancel: return this.cancel();
        }
        if(pos.y < 0 || pos.y > 2) { return false; }
        if(isEnter) {
            return this.click(pos);
        } else {
            return this.mouseMove(pos);
        }
    }
};<|endoftext|>


<commit_before>/*
Copyright 2015 OpenMarket Ltd

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.
*/

'use strict';

var React = require('react');

var MRoomMemberTileController = require('matrix-react-sdk/lib/controllers/molecules/MRoomMemberTile');

var sdk = require('matrix-react-sdk');

module.exports = React.createClass({
    displayName: 'MRoomMemberTile',
    mixins: [MRoomMemberTileController],

    getMemberEventText: function() {
        var ev = this.props.mxEvent;
        // XXX: SYJS-16
        var senderName = ev.sender ? ev.sender.name : "Someone";
        switch (ev.getContent().membership) {
            case 'invite':
                return senderName + " invited " + ev.target.name + ".";
            case 'join':
                return senderName + " joined the room.";
            case 'leave':
                return senderName + " left the room.";
        }
    },

    render: function() {
        var MessageTimestamp = sdk.getComponent('atoms.MessageTimestamp');
        // XXX: for now, just cheekily borrow the css from message tile...
        return (

                <MessageTimestamp ts={this.props.mxEvent.getTs()} />

                    {this.getMemberEventText()}

        );
    },
});

<commit_msg>port member tile fixes<commit_after>/*
Copyright 2015 OpenMarket Ltd

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.
*/

'use strict';

var React = require('react');

var MRoomMemberTileController = require("matrix-react-sdk/lib/controllers/molecules/MRoomMemberTile");

var MatrixClientPeg = require("matrix-react-sdk/lib/MatrixClientPeg");
var sdk = require('matrix-react-sdk');
var TextForEvent = require('matrix-react-sdk/lib/TextForEvent');

module.exports = React.createClass({
    displayName: 'MRoomMemberTile',
    mixins: [MRoomMemberTileController],

    getMemberEventText: function() {
        return TextForEvent.textForEvent(this.props.mxEvent);
    },

    render: function() {
        var MessageTimestamp = sdk.getComponent('atoms.MessageTimestamp');
        var MemberAvatar = sdk.getComponent('atoms.MemberAvatar');
        // XXX: for now, just cheekily borrow the css from message tile...
        var timestamp = this.props.last ? <MessageTimestamp ts={this.props.mxEvent.getTs()} /> : null;
        var text = this.getMemberEventText();
        if (!text) return
;
        return (

                    <MemberAvatar member={this.props.mxEvent.sender} />

                { timestamp }

                    { text }

        );
    },
});

<|endoftext|>


<commit_before>#!/usr/bin/env node

var swig = require('../index'),
  optimist = require('optimist'),
  fs = require('fs'),
  path = require('path'),
  filters = require('../lib/filters'),
  utils = require('../lib/utils'),
  uglify = require('uglify-js');

var command,
  argv = optimist
    .usage('\n Usage:\n' +
      '    $0 compile [files] [options]\n' +
      '    $0 run [files] [options]\n' +
      '    $0 render [files] [options]\n'
      )
    .describe({
      v: 'Show the Swig version number.',
      o: 'Output location.',
      h: 'Show this help screen.',
      j: 'Variable context as a JSON file.',
      c: 'Variable context as a CommonJS-style file. Used only if option `j` is not provided.',
      m: 'Minify compiled functions with uglify-js',
      'wrap-start': 'Template wrapper beginning for "compile".',
      'wrap-end': 'Template wrapper end for "compile".',
      'method-name': 'Method name to set template to and run from.'
    })
    .alias('v', 'version')
    .alias('o', 'output')
    .default('o', 'stdout')
    .alias('h', 'help')
    .alias('j', 'json')
    .alias('c', 'context')
    .alias('m', 'minify')
    .default('wrap-start', 'var tpl = ')
    .default('wrap-end', ';')
    .default('method-name', 'tpl')
    .check(function (argv) {
      if (argv.v) {
        return;
      }

      if (!argv._.length) {
        throw new Error('');
      }

      command = argv._.shift();
      if (command !== 'compile' && command !== 'render' && command !== 'run') {
        throw new Error('Unrecognized command "' + command + '". Use -h for help.');
      }

      if (argv['method-name'] !== 'tpl' && argv['wrap-start'] !== 'var tpl =') {
        throw new Error('Cannot use arguments "--method-name" and "--wrap-start" together.');
      }

      if (argv['method-name'] !== 'tpl') {
        argv['wrap-start'] = 'var ' + argv['method-name'] + ' = ';
      }
    })
    .argv,
  ctx = {},
  out = function (file, str) {
    console.log(str);
  },
  efn = function () {},
  anonymous,
  files,
  fn;

if (argv.v) {
  console.log(require('../package').version);
  process.exit(0);
}

if (argv.j) {
  ctx = JSON.parse(fs.readFileSync(argv.j, 'utf8'));
} else if (argv.c) {
  ctx = require(argv.c);
}

if (argv.o !== 'stdout') {
  argv.o += '/';
  argv.o = path.normalize(argv.o);

  try {
    fs.mkdirSync(argv.o);
  } catch (e) {
    if (e.errno !== 47) {
      throw e;
    }
  }

  out = function (file, str) {
    file = path.basename(file);
    fs.writeFileSync(argv.o + file, str);
    console.log('Wrote', argv.o + file);
  };
}

switch (command) {
case 'compile':
  fn = function (file, str) {
    var r = swig.precompile(str, { filename: file, locals: ctx }).tpl.toString().replace('anonymous', '');

    r = argv['wrap-start'] + r + argv['wrap-end'];

    if (argv.m) {
      r = uglify.minify(r, { fromString: true }).code;
    }

    out(file, r);
  };
  break;

case 'run':
  fn = function (file, str) {
    (function () {
      eval(str);
      var __tpl = eval(argv['method-name']);
      out(file, __tpl(swig, ctx, filters, utils, efn));
    }());
  };
  break;

case 'render':
  fn = function (file, str) {
    out(file, swig.render(str, { filename: file, locals: ctx }));
  };
  break;
}

argv._.forEach(function (file) {
  var str = fs.readFileSync(file, 'utf8');
  fn(file, str);
});
<commit_msg>Added arguments for custom tags and filters to CLI<commit_after>#!/usr/bin/env node

var swig = require('../index'),
  optimist = require('optimist'),
  fs = require('fs'),
  path = require('path'),
  filters = require('../lib/filters'),
  utils = require('../lib/utils'),
  uglify = require('uglify-js');

var command,
  argv = optimist
    .usage('\n Usage:\n' +
      '    $0 compile [files] [options]\n' +
      '    $0 run [files] [options]\n' +
      '    $0 render [files] [options]\n'
      )
    .describe({
      v: 'Show the Swig version number.',
      o: 'Output location.',
      h: 'Show this help screen.',
      j: 'Variable context as a JSON file.',
      c: 'Variable context as a CommonJS-style file. Used only if option `j` is not provided.',
      m: 'Minify compiled functions with uglify-js',
      'filters': 'File containing custom filters',
      'tags': 'File containing custom tags',
      'wrap-start': 'Template wrapper beginning for "compile".',
      'wrap-end': 'Template wrapper end for "compile".',
      'method-name': 'Method name to set template to and run from.'
    })
    .alias('v', 'version')
    .alias('o', 'output')
    .default('o', 'stdout')
    .alias('h', 'help')
    .alias('j', 'json')
    .alias('c', 'context')
    .alias('m', 'minify')
    .default('wrap-start', 'var tpl = ')
    .default('wrap-end', ';')
    .default('method-name', 'tpl')
    .check(function (argv) {
      if (argv.v) {
        return;
      }

      if (!argv._.length) {
        throw new Error('');
      }

      command = argv._.shift();
      if (command !== 'compile' && command !== 'render' && command !== 'run') {
        throw new Error('Unrecognized command "' + command + '". Use -h for help.');
      }

      if (argv['method-name'] !== 'tpl' && argv['wrap-start'] !== 'var tpl =') {
        throw new Error('Cannot use arguments "--method-name" and "--wrap-start" together.');
      }

      if (argv['method-name'] !== 'tpl') {
        argv['wrap-start'] = 'var ' + argv['method-name'] + ' = ';
      }
    })
    .argv,
  ctx = {},
  out = function (file, str) {
    console.log(str);
  },
  efn = function () {},
  anonymous,
  files,
  fn;

if (argv.v) {
  console.log(require('../package').version);
  process.exit(0);
}

if (argv.j) {
  ctx = JSON.parse(fs.readFileSync(argv.j, 'utf8'));
} else if (argv.c) {
  ctx = require(argv.c);
}

if (argv.o !== 'stdout') {
  argv.o += '/';
  argv.o = path.normalize(argv.o);

  try {
    fs.mkdirSync(argv.o);
  } catch (e) {
    if (e.errno !== 47) {
      throw e;
    }
  }

  out = function (file, str) {
    file = path.basename(file);
    fs.writeFileSync(argv.o + file, str);
    console.log('Wrote', argv.o + file);
  };
}

if (argv.filters) {
  var cfilters,
    k;

  argv.filters = path.resolve(argv.filters);
  cfilters = require(argv.filters);

  for (k in cfilters) {
    if (cfilters.hasOwnProperty(k)) {
      swig.setFilter(k, cfilters[k]);
    }
  }
}

if (argv.tags) {
  var ctags,
    k;

  argv.tags = path.resolve(argv.tags);
  ctags = require(argv.tags);

  for (k in ctags) {
    if (ctags.hasOwnProperty(k)) {
      var parse = ctags[k].parse,
        compile = ctags[k].compile,
        ends = ctags[k].ends,
        block = ctags[k].blockLevel;
      swig.setTag(k, parse, compile, ends, block);
    }
  }
}

switch (command) {
case 'compile':
  fn = function (file, str) {
    var r = swig.precompile(str, { filename: file, locals: ctx }).tpl.toString().replace('anonymous', '');

    r = argv['wrap-start'] + r + argv['wrap-end'];

    if (argv.m) {
      r = uglify.minify(r, { fromString: true }).code;
    }

    out(file, r);
  };
  break;

case 'run':
  fn = function (file, str) {
    (function () {
      eval(str);
      var __tpl = eval(argv['method-name']);
      out(file, __tpl(swig, ctx, filters, utils, efn));
    }());
  };
  break;

case 'render':
  fn = function (file, str) {
    out(file, swig.render(str, { filename: file, locals: ctx }));
  };
  break;
}

argv._.forEach(function (file) {
  var str = fs.readFileSync(file, 'utf8');
  fn(file, str);
});
<|endoftext|>


<commit_before>var userController = require('./userController');
var User = require('./userModel');

module.exports = function (app) {
  // app.param('id', userController.getUserData);
  app.get('/users', function(req, res, next) {
    User.find({}).exec(function(err, data) {
      if (err) {
        res.send(500, err);
      } else {
        res.json(data);
      }
    });
  });
  // app.get('/users/:id', userController.userProfile);

  // app.get('/users', userController.getAllUsers);

  app.post('/users', function(req, res) {
    var data = req.body;
    var addUser = User.create({
      username: data.username,
    }, function(err,  newUser) {
      if (err) {
        res.sendStatus(500, err);
      }
      res.send(newUser);
    });
  });

};<commit_msg>(feat)access user obj using username<commit_after>var userController = require('./userController');
var User = require('./userModel');

module.exports = function (app) {
  // app.param('id', function(req, res, next, id) {
  //   User.findById(id).exec(function(err, data) {
  //     if (err) {
  //       res.send(500, err);
  //     } else {
  //       req.userData = data;
  //       next();
  //     }
  //   });
  // });

  // app.get('/users/:id', function(req, res, next) {
  //   res.status(200);
  //   res.send(req.userData);
  // });

  app.get('/users/:username', function(req, res, next){
    var username = req.params.username;
    User.findOne({'username': username}).exec(function(err, user){
      res.send(user);
    });
  });

  app.get('/users', function(req, res, next) {
    User.find({}).exec(function(err, data) {
      if (err) {
        res.send(500, err);
      } else {
        res.json(data);
      }
    });
  });

  app.post('/users', function(req, res) {
    var data = req.body;
    var addUser = User.create({
      username: data.username,
    }, function(err,  newUser) {
      if (err) {
        res.sendStatus(500, err);
      }
      res.send(newUser);
    });
  });

};<|endoftext|>


<commit_before>import React from 'react';

import gql from 'graphql-tag'
import {
  apolloForm
} from '../lib/src/index';

const defs = gql`
  enum State {
    NOT_FOUND
    ACTIVE
    INACTIVE
    SUSPENDED
  }
  input TextArea {
    value: String
  }
`;

export const query = gql`
  mutation createPost($title: String!, $isDraft: Boolean, $views: Int, $average: Float, $state: State, $content: TextArea) {
    createPost(title: $title, isDraft: $isDraft, views: $views, average: $average, state: $state, content: $content) {
      id
      createdAt
    }
  }`;
const CreatePostForm = apolloForm(query, {
  defs,
  resolvers: {
    TextArea: { component: 'textarea' }
  },
});

export default CreatePostForm;
<commit_msg>+ datepicker to user story<commit_after>import React from 'react';

import gql from 'graphql-tag'
import {
  apolloForm
} from '../lib/src/index';

const defs = gql`
  enum State {
    NOT_FOUND
    ACTIVE
    INACTIVE
    SUSPENDED
  }
  input TextArea {
    value: String
  }
  input Date {
    value: String
  }
`;

export const query = gql`
  mutation createPost($title: String!, $isDraft: Boolean, $views: Int, $average: Float, $state: State, $content: TextArea, $publishedAt: Date) {
    createPost(title: $title, isDraft: $isDraft, views: $views, average: $average, state: $state, content: $content, publishedAt: $publishAt) {
      id
      createdAt
    }
  }`;
const CreatePostForm = apolloForm(query, {
  defs,
  resolvers: {
    TextArea: { component: 'textarea' },
    Date: { component: 'input', type: 'date' }
  },
});

export default CreatePostForm;
<|endoftext|>


<commit_before>(function () {

	var ReverseRegexThing = function () {
		this.REGEX_TOKEN_REGEX = /\\.|\((?:\?.)?|\)|\||\^|\$|\[\^?(?:\\.|[^\]])+\]|[\?\*\+][\?\+]?|\{\d+(?:,(?:\d+))?\}|\.|[^\\\.\?\*\+\(\)\{\}\[\]\^\$\|]+/g;
		this.BRACE_QUANTIFIER_REGEX = /^\{\d+(?:,(?:\d+))?\}$/;

		// Since some of the quantifiers permit arbitrarily large numbers, let's pick a reasonably big one and go with that.
		this.PSEUDOINFINITY = 100;

		this.WHITESPACE_CLASS = [' ', '\t'];

		this.parse = function (regex) {
			var tokens = regex.match(this.REGEX_TOKEN_REGEX);
			var structure = this.parseStructure(tokens, 0, 0).structure;

			return structure;
		};

		this.parseStructure = function(tokenArray, startIndex, parenthesisLevel) {
			var structure = {tokenType: "or", operands: [{tokenType: "and", operands: []}]};
			var substructure = structure.operands[0];

			for (var i = startIndex; i < tokenArray.length; i++) {
				var token = tokenArray[i];
				if (token[0] === "(") {
					var innerStructure = this.parseStructure(tokenArray, i + 1, parenthesisLevel + 1);
					substructure.operands.push(innerStructure.structure);
					i = innerStructure.lastIndex;
				} else if (token === ")" && parenthesisLevel > 0) {
					while (structure.operands && structure.operands.length === 1) {
						structure = structure.operands[0];
					}
					return {structure: structure, lastIndex: i};
				} else {
					var parsedToken = this.parseToken(token);
					if (parsedToken.tokenType === "pipe") {
						substructure = {tokenType: "and", operands: []};
						structure.operands.push(substructure);
					} else if (parsedToken.tokenType === "quantifier") {
						var modifiedToken = substructure.operands[substructure.operands.length - 1];
						if (modifiedToken.tokenType === "literal" && modifiedToken.content.length > 1) {
							var newToken = {
								tokenType: "literal",
								content: modifiedToken.content[modifiedToken.content.length - 1],
								multiplicity: parsedToken
							};
							modifiedToken.content = modifiedToken.content.substring(0, modifiedToken.content.length - 1);
							substructure.operands.push(newToken);
						} else {
							modifiedToken.multiplicity = parsedToken;
						}
					} else {
						substructure.operands.push(parsedToken);
					}
				}
			}

			if (parenthesisLevel > 0) throw "Unclosed parenthesis in expression!!!11";

			while (structure.operands && structure.operands.length === 1) {
				structure = structure.operands[0];
			}

			return {structure: structure, lastIndex: null};
		};

		this.parseToken = function (inToken) {
			switch (inToken[0]) {
				case '|':
					return {tokenType: "pipe"};
				case '[':
					return this.parseBracketClass(inToken);
				case '\\':
					return this.parseEscapedCharacter(inToken);
				case '?':
				case '*':
				case '+':
				case '{':
					return this.parseQuantifier(inToken);
				case '^':
					return {tokenType: "start"};
				case '$':
					return {tokenType: "end"};
				case '.':
					return {tokenType: "wildcard"};
				default:
					return {tokenType: "literal", content: inToken};
			}
		};

		this.parseBracketClass = function (inToken) {
			var outToken = {tokenType: "or", operands: []};
			var startIndex = 1;
			if (inToken[startIndex] === "^") {
				startIndex++;
				outToken.tokenType = "nor";
			}
			if (inToken[startIndex] === "-") {
				startIndex++;
				outToken.operands.push("-");
			}
			for (var i = startIndex; i < inToken.length - 1; i++) {
				if (inToken[i] === "\\") {
					i++;
					outToken.operands.push(inToken[i]);
				} else if (inToken[i] === "-" && i < inToken.length - 2) {
					var first = outToken.operands.pop();
					var last = inToken[i + 1];
					var range = {tokenType: "range", from: first, to: last};
					outToken.operands.push(range);
					i++;
				} else {
					outToken.operands.push(inToken[i]);
				}
			}
			return outToken;
		};

		this.parseQuantifier = function (inToken) {
			var outToken = {tokenType: "quantifier"};

			switch (inToken[0]) {
				case '?':
					outToken.min = 0;
					outToken.max = 1;
					break;
				case '*':
					outToken.min = 0;
					outToken.max = this.PSEUDOINFINITY;
					break;
				case '+':
					outToken.min = 1;
					outToken.max = this.PSEUDOINFINITY;
					break;
				case '{':
					if (!this.BRACE_QUANTIFIER_REGEX.test(inToken)) throw "That's a stupid quantifier and you're stupid, stupid.";

					// TODO Add some fuck*ng error handling here. :\
					var subTokens = inToken.match(/,|[0-9]+/g);
					outToken.min = subTokens[0];
					outToken.max = (subTokens.length === 2 ? this.PSEUDOINFINITY : subTokens[subTokens.length - 1]); // Clear as day. ^_^
					break;
				default:
					throw "Invalid quantifier, somehow: " + inToken;
			}

			if (inToken.length > 1) {
				switch (inToken[inToken.length - 1]) {
					case '?':
						outToken.lazy = true;
						break;
					case '+':
						outToken.greedy = true;
						break;
				}
			}

			return outToken;
		};

		this.parseEscapedCharacter = function (inToken) {
			switch (inToken[1]) {
				case 'd':
					return {tokenType: "or", operands: this.DIGIT_CLASS};
				case 'D':
					return {tokenType: "nor", operands: this.DIGIT_CLASS};
				case 'n':
					return {tokenType: "literal", content: '\n'};
				case 's':
					return {tokenType: "or", operands: this.WHITESPACE_CLASS};
				case 'S':
					return {tokenType: "nor", operands: this.WHITESPACE_CLASS};
				case 't':
					return {tokenType: "literal", content: '\t'};
				case 'w':
					return {tokenType: "or", operands: this.WORD_CLASS};
				case 'W':
					return {tokenType: "nor", operands: this.WORD_CLASS};
				default:
					return {tokenType: "literal", content: inToken[1]};
			}
		};

		this.parseOutput = function () {
			var regexString = document.getElementById("regex").value;
			var regexStructure = this.parse(regexString);
			window.debugStructure = regexStructure;
			document.getElementById("structureOutput").innerHTML = this.prettyPrint(regexStructure, 0);
		};

		this.prettyPrint = function (structure, level) {
			var outString = "";
			if (typeof structure === "object") {
				if (structure[0]) {
					outString += "[";
					for (var i = 0; i < structure.length; i++) {
						outString += this.prettyPrint(structure[i], level + 1) + ", ";
					}
					outString += "]";
				} else {
					if (structure.tokenType === "literal" && !structure.multiplicity) {
						outString += structure.content;
					} else {
						outString += "{";
						for (var k in structure) {
							outString += k + ": ";
							outString += this.prettyPrint(structure[k], level + 1) + ", ";
						}
						outString += "}";
					}
				}
			} else {
				outString += "\"" + structure + "\"?!!";
			}
			return outString;
		};

		this.generateExample = function (structure) {
			var example = "";
			for (var i = 0; i < structure.length; i++) {
				var token = structure[i];
				if (token[0]) {
					example += this.generateExample(token);
				}
				var multiplicity = 1;
				if (token.multiplicity) {
					var min = token.multiplicity.min;
					var max = token.multiplicity.max;
					multiplicity = min + Math.floor(Math.random() * (max - min + 1));
				}
				for (var j = 0; j < multiplicity; j++) {
					switch (token.tokenType) {
						case "literal":
							example += token.content;
							break;
						case "wildcard":
							example += String.fromCharCode(Math.floor(Math.random() * 256));
							break;
						default:
							// not yet implemented
					}
				}
			}

			return example;
		};

	};

	window.ReverseRegexThing = ReverseRegexThing;
}());<commit_msg>Renamed parseOutput to outputStructure and rewrote prettyPrint to use HTML rather than try to make it look like formatted JSON.<commit_after>(function () {

	var ReverseRegexThing = function () {
		this.REGEX_TOKEN_REGEX = /\\.|\((?:\?.)?|\)|\||\^|\$|\[\^?(?:\\.|[^\]])+\]|[\?\*\+][\?\+]?|\{\d+(?:,(?:\d+))?\}|\.|[^\\\.\?\*\+\(\)\{\}\[\]\^\$\|]+/g;
		this.BRACE_QUANTIFIER_REGEX = /^\{\d+(?:,(?:\d+))?\}$/;

		// Since some of the quantifiers permit arbitrarily large numbers, let's pick a reasonably big one and go with that.
		this.PSEUDOINFINITY = 100;

		this.WHITESPACE_CLASS = [' ', '\t'];

		this.parse = function (regex) {
			var tokens = regex.match(this.REGEX_TOKEN_REGEX);
			var structure = this.parseStructure(tokens, 0, 0).structure;

			return structure;
		};

		this.parseStructure = function(tokenArray, startIndex, parenthesisLevel) {
			var structure = {tokenType: "or", operands: [{tokenType: "and", operands: []}]};
			var substructure = structure.operands[0];

			for (var i = startIndex; i < tokenArray.length; i++) {
				var token = tokenArray[i];
				if (token[0] === "(") {
					var innerStructure = this.parseStructure(tokenArray, i + 1, parenthesisLevel + 1);
					substructure.operands.push(innerStructure.structure);
					i = innerStructure.lastIndex;
				} else if (token === ")" && parenthesisLevel > 0) {
					while (structure.operands && structure.operands.length === 1) {
						structure = structure.operands[0];
					}
					return {structure: structure, lastIndex: i};
				} else {
					var parsedToken = this.parseToken(token);
					if (parsedToken.tokenType === "pipe") {
						substructure = {tokenType: "and", operands: []};
						structure.operands.push(substructure);
					} else if (parsedToken.tokenType === "quantifier") {
						var modifiedToken = substructure.operands[substructure.operands.length - 1];
						if (modifiedToken.tokenType === "literal" && modifiedToken.content.length > 1) {
							var newToken = {
								tokenType: "literal",
								content: modifiedToken.content[modifiedToken.content.length - 1],
								multiplicity: parsedToken
							};
							modifiedToken.content = modifiedToken.content.substring(0, modifiedToken.content.length - 1);
							substructure.operands.push(newToken);
						} else {
							modifiedToken.multiplicity = parsedToken;
						}
					} else {
						substructure.operands.push(parsedToken);
					}
				}
			}

			if (parenthesisLevel > 0) throw "Unclosed parenthesis in expression!!!11";

			while (structure.operands && structure.operands.length === 1) {
				structure = structure.operands[0];
			}

			return {structure: structure, lastIndex: null};
		};

		this.parseToken = function (inToken) {
			switch (inToken[0]) {
				case '|':
					return {tokenType: "pipe"};
				case '[':
					return this.parseBracketClass(inToken);
				case '\\':
					return this.parseEscapedCharacter(inToken);
				case '?':
				case '*':
				case '+':
				case '{':
					return this.parseQuantifier(inToken);
				case '^':
					return {tokenType: "start"};
				case '$':
					return {tokenType: "end"};
				case '.':
					return {tokenType: "wildcard"};
				default:
					return {tokenType: "literal", content: inToken};
			}
		};

		this.parseBracketClass = function (inToken) {
			var outToken = {tokenType: "or", operands: []};
			var startIndex = 1;
			if (inToken[startIndex] === "^") {
				startIndex++;
				outToken.tokenType = "nor";
			}
			if (inToken[startIndex] === "-") {
				startIndex++;
				outToken.operands.push("-");
			}
			for (var i = startIndex; i < inToken.length - 1; i++) {
				if (inToken[i] === "\\") {
					i++;
					outToken.operands.push(inToken[i]);
				} else if (inToken[i] === "-" && i < inToken.length - 2) {
					var first = outToken.operands.pop();
					var last = inToken[i + 1];
					var range = {tokenType: "range", from: first, to: last};
					outToken.operands.push(range);
					i++;
				} else {
					outToken.operands.push(inToken[i]);
				}
			}
			return outToken;
		};

		this.parseQuantifier = function (inToken) {
			var outToken = {tokenType: "quantifier"};

			switch (inToken[0]) {
				case '?':
					outToken.min = 0;
					outToken.max = 1;
					break;
				case '*':
					outToken.min = 0;
					outToken.max = this.PSEUDOINFINITY;
					break;
				case '+':
					outToken.min = 1;
					outToken.max = this.PSEUDOINFINITY;
					break;
				case '{':
					if (!this.BRACE_QUANTIFIER_REGEX.test(inToken)) throw "That's a stupid quantifier and you're stupid, stupid.";

					// TODO Add some fuck*ng error handling here. :\
					var subTokens = inToken.match(/,|[0-9]+/g);
					outToken.min = subTokens[0];
					outToken.max = (subTokens.length === 2 ? this.PSEUDOINFINITY : subTokens[subTokens.length - 1]); // Clear as day. ^_^
					break;
				default:
					throw "Invalid quantifier, somehow: " + inToken;
			}

			if (inToken.length > 1) {
				switch (inToken[inToken.length - 1]) {
					case '?':
						outToken.lazy = true;
						break;
					case '+':
						outToken.greedy = true;
						break;
				}
			}

			return outToken;
		};

		this.parseEscapedCharacter = function (inToken) {
			switch (inToken[1]) {
				case 'd':
					return {tokenType: "or", operands: this.DIGIT_CLASS};
				case 'D':
					return {tokenType: "nor", operands: this.DIGIT_CLASS};
				case 'n':
					return {tokenType: "literal", content: '\n'};
				case 's':
					return {tokenType: "or", operands: this.WHITESPACE_CLASS};
				case 'S':
					return {tokenType: "nor", operands: this.WHITESPACE_CLASS};
				case 't':
					return {tokenType: "literal", content: '\t'};
				case 'w':
					return {tokenType: "or", operands: this.WORD_CLASS};
				case 'W':
					return {tokenType: "nor", operands: this.WORD_CLASS};
				default:
					return {tokenType: "literal", content: inToken[1]};
			}
		};

		this.outputStructure = function () {
			var regexString = document.getElementById("regex").value;
			var regexStructure = this.parse(regexString);
			window.debugStructure = regexStructure;
			document.getElementById("structureOutput").innerHTML = this.prettyPrint(regexStructure, 0);
		};

		this.prettyPrint = function (structure, level) {
			var outString = "";
			if (typeof structure === "object") {
				if (structure[0]) {
					outString += "
";
					for (var i = 0; i < structure.length; i++) {
						outString += "
" + this.prettyPrint(structure[i], level + 1) + "
";
					}
					outString += "
";
				} else {
					if (structure.tokenType === "literal") {
						outString += structure.content + (structure.multiplicity ? " {" + structure.multiplicity.min + "," + structure.multiplicity.max + "}" : "");
					} else {
						outString += "
";
						outString += "
" + structure.tokenType + (structure.multiplicity ? " {" + structure.multiplicity.min + "," + structure.multiplicity.max + "}" : "") + "
";
						outString += "
" + this.prettyPrint(structure.operands, level + 1) + "
";
						outString += "
";
					}
				}
			} else {
				outString += "\"" + structure + "\"?!!";
			}
			return outString;
		};

		this.generateExample = function (structure) {
			var example = "";
			for (var i = 0; i < structure.length; i++) {
				var token = structure[i];
				if (token[0]) {
					example += this.generateExample(token);
				}
				var multiplicity = 1;
				if (token.multiplicity) {
					var min = token.multiplicity.min;
					var max = token.multiplicity.max;
					multiplicity = min + Math.floor(Math.random() * (max - min + 1));
				}
				for (var j = 0; j < multiplicity; j++) {
					switch (token.tokenType) {
						case "literal":
							example += token.content;
							break;
						case "wildcard":
							example += String.fromCharCode(Math.floor(Math.random() * 256));
							break;
						default:
							// not yet implemented
					}
				}
			}

			return example;
		};

	};

	window.ReverseRegexThing = ReverseRegexThing;
}());<|endoftext|>


<commit_before>var Service = require("./../service.js");
var serviceFactory = require("../service-factory");

function DeviceService(deviceId, serviceId, queen) {
    var deviceService = {}
        , _queen = queen
        , _characteristics = {
            'services' : 0
        }
        , _requests = {
            'discover-services' : function () {
                return {
                    packetType : 'read'
                    , packetData :[_characteristics['services']]
                }
            }
        }
        , _responses = {};

    _responses[_characteristics['services']] = function (servicesBuffer) {
        var supportedServiceCount = servicesBuffer[0];

        servicesBuffer = servicesBuffer.slice(1);

        for(var i=0; i< supportedServiceCount; ++i) {
            var serviceId = servicesBuffer[0]
                , profileId = servicesBuffer.slice(1, 4)
                , serviceFunction = serviceFactory.getService(profileId);

            _queen.addService(serviceFunction(deviceId, serviceId));

            servicesBuffer = servicesBuffer.slice(3);
        }

        return {
            response : 'discover-services'
            , data : "service discovery finished"
        };
    };

    deviceService.__proto__ = Service(
        deviceId
        , serviceId
        , _requests
        , _responses
    );

    return deviceService;
}

module.exports = DeviceService;

(function(){
    var assert = require("assert");
    var ResponsePacket = require("../device-packets/response-packet");

    var deviceId = "my-device-id"
        , serviceId = 3
        , service
        , queen = {
            addService : function(s) {
                service = s;
            }
        }
        , deviceService = DeviceService(deviceId, serviceId, queen);

    (function(){
        console.log("Should process discover-services json message.");
        assert.deepEqual(
            deviceService.processRequest(JSON.stringify(
                {request: 'discover-services'}
            )),
            new Buffer([1, 1, 0, 0, 0, serviceId, 1, 0])
        );
    })();
    (function(){
        console.log("Should process discover-services response packet.");

        var switchServiceId = 8;

        assert.deepEqual(
            deviceService.processResponse(ResponsePacket(
                Buffer.concat([
                    new Buffer([
                        1, 4, 0, 0, 0, serviceId, 1, 0, 5, 1, switchServiceId
                    ])
                    , new Buffer("SWH")
                ])
            )),
            [{response: 'discover-services', data: "service discovery finished"}]
        );
        assert.equal(service.deviceId(), deviceId);
        assert.equal(service.serviceId(), switchServiceId);
    })();
})(this);<commit_msg>Achal | Sending back the discovered services information to webapp.<commit_after>var Service = require("./../service.js");
var serviceFactory = require("../service-factory");

function DeviceService(deviceId, serviceId, queen) {
    var deviceService = {}
        , _queen = queen
        , _characteristics = {
            'services' : 0
        }
        , _requests = {
            'discover-services' : function () {
                return {
                    packetType : 'read'
                    , packetData :[_characteristics['services']]
                }
            }
        }
        , _responses = {};

    _responses[_characteristics['services']] = function (servicesBuffer) {
        var supportedServiceCount = servicesBuffer[0]
            , addedServices = [];

        servicesBuffer = servicesBuffer.slice(1);

        for(var i=0; i< supportedServiceCount; ++i) {
            var serviceId = servicesBuffer[0]
                , profileId = servicesBuffer.slice(1, 4)
                , serviceFunction = serviceFactory.getService(profileId)
                , service = serviceFunction(deviceId, serviceId);

            addedServices.push(
                {
                    serviceAddress : service.serviceAddress()
                    , profileId : profileId.toString()
                }
            );

            _queen.addService(service);

            servicesBuffer = servicesBuffer.slice(3);
        }

        return {
            response : 'discover-services'
            , data : addedServices
        };
    };

    deviceService.__proto__ = Service(
        deviceId
        , serviceId
        , _requests
        , _responses
    );

    return deviceService;
}

module.exports = DeviceService;

(function(){
    var assert = require("assert");
    var ResponsePacket = require("../device-packets/response-packet");

    var deviceId = "my-device-id"
        , serviceId = 3
        , service
        , queen = {
            addService : function(s) {
                service = s;
            }
        }
        , deviceService = DeviceService(deviceId, serviceId, queen);

    (function(){
        console.log("Should process discover-services json message.");
        assert.deepEqual(
            deviceService.processRequest(JSON.stringify(
                {request: 'discover-services'}
            )),
            new Buffer([1, 1, 0, 0, 0, serviceId, 1, 0])
        );
    })();
    (function(){
        console.log("Should process discover-services response packet.");

        var switchServiceId = 8
            , profileId = "SWH";

        assert.deepEqual(
            deviceService.processResponse(ResponsePacket(
                Buffer.concat([
                    new Buffer([
                        1, 4, 0, 0, 0, serviceId, 1, 0, 5, 1, switchServiceId
                    ])
                    , new Buffer(profileId)
                ])
            )),
            [{
                response: 'discover-services'
                , data: [
                    {
                        serviceAddress: deviceId + ':' + switchServiceId
                        , profileId: profileId
                    }
                ]
            }]
        );
        assert.equal(service.deviceId(), deviceId);
        assert.equal(service.serviceId(), switchServiceId);
    })();
})(this);<|endoftext|>


<commit_before>( function ( exports, undefined ) {

var	global = this,
	autochthon = global.Z,
	noConflict = function () {
		global.Z = autochthon;
		return this;
	},
	version = exports.version =
		'0.1.0',

	toString = exports.toString =
		Object.prototype.toString,

	hasOwn = exports.hasOwn =
		Object.prototype.hasOwnProperty,

	/** jQuery-style "trim", using native method on `String.prototype` if available. */
	trim = exports.trim =
		String.prototype.trim ?
			function ( text ) { return text == null ? '' : String.prototype.trim.call( text ); } :
			function ( text ) { return text == null ? '' : text.toString().replace( /^\s+/, '' ).replace( /\s+$/, '' ); },

	slice = exports.slice =
		Array.prototype.slice;

/**
 * General-purpose empty function. May also be deemed suitable as a unique alternative "nil" type for
 * strict-equal matches whenever it's desirable to avoid traditional `null` and `undefined`.
 */
function noop () {}
exports.noop = noop;

/**
 * Like `noop`, except suited for substitution on methods designed to be chainable.
 */
function getThis () { return this; }
exports.getThis = getThis;

/**
 * Calls the specified native function if it exists and returns its result; if no such function exists on
 * `obj` as registered in `__native.fn`, returns our unique `noop` (as opposed to `null` or `undefined`,
 * which may be a valid result from the native function itself).
 */
function __native ( item, obj /* , ... */ ) {
	var n = __native.fn[item];
	return n && obj[item] === n ? n.apply( obj, slice.call( arguments, 2 ) ) : noop;
}
( exports.__native = __native ).fn = {
	forEach: Array.prototype.forEach
};

/**
 * A more browser-safe alternative to `typeof`, implemented similarly to jQuery and elsewhere, which
 * checks the string output returned by a plain `toString()` call.
 */
function type ( obj ) {
	return obj == null ? String( obj ) : type.map[ toString.call( obj ) ] || 'object';
}
( exports.type = type ).map = {};
each( 'Array Boolean Date Function Number Object RegExp String'.split(' '), function( i, name ) {
	type.map[ "[object " + name + "]" ] = name.toLowerCase();
});

/** isNumber */
function isNumber ( n ) { return !isNaN( parseFloat( n ) && isFinite( n ) ); }
exports.isNumber = isNumber;

/** isArray */
function isArray ( obj ) { return type( obj ) === 'array'; }
exports.isArray = isArray;

/** isFunction */
function isFunction ( obj ) { return type( obj ) === 'function'; }
exports.isFunction = isFunction;

/**
 * Near-straight port of jQuery `isPlainObject`
 */
function isPlainObject ( obj ) {
	var key;
	if ( !obj || type( obj ) !== 'object' || obj.nodeType || obj === global ||
		obj.constructor &&
		!hasOwn.call( obj, 'constructor' ) &&
		!hasOwn.call( obj.constructor.prototype, 'isPrototypeOf' )
	) {
		return false;
	}
	for ( key in obj ) {}
	return key === undefined || hasOwn.call( obj, key );
}
exports.isPlainObject = isPlainObject;

/**
 * Returns a boolean indicating whether the object or array at `obj` contains any members. For an
 * `Object` type, if `andPrototype` is included and truthy, `obj` must be empty throughout its
 * prototype chain as well.
 */
function isEmpty ( obj, andPrototype ) {
	var key;
	if ( isArray( obj ) && obj.length ) {
		return false;
	}
	for ( key in obj ) {
		if ( andPrototype || hasOwn.call( obj, key ) ) {
			return false;
		}
	}
	return true;
}
exports.isEmpty = isEmpty;

/**
 * jQuery-style `$.each`, with callback signature of `key, value, object`.
 */
function each ( obj, fn ) {
	if ( !obj ) { return; }
	var	key, i, l = obj.length;
	if ( l === undefined || isFunction( obj ) ) {
		for ( key in obj ) {
			if ( fn.call( obj[key], key, obj[key], obj ) === false ) {
				break;
			}
		}
	} else {
		for ( i = 0, l = obj.length; i < l; ) {
			if ( fn.call( obj[i], i, obj[i++], obj ) === false ) {
				break;
			}
		}
	}
	return obj;
}
exports.each = each;

/**
 * ES5-style `Object.forEach`, with callback signature of `value, key, object`.
 */
function forEach ( obj, fn, context ) {
	var	n, l, key, i;
	if ( obj == null ) { return; }
	if ( ( n = __native( 'forEach', obj, fn, context ) ) !== noop ) { return n; }
	if ( ( l = obj.length ) === undefined || isFunction( obj ) ) {
		for ( key in obj ) {
			if ( fn.call( context || obj[key], obj[key], key, obj ) === false ) {
				break;
			}
		}
	} else {
		for ( i = 0, l = obj.length; i < l; ) {
			if ( fn.call( context || obj[i], obj[i], i++, obj ) === false ) {
				break;
			}
		}
	}
	return obj;
}
exports.forEach = forEach;

/**
 * Near-straight port of jQuery `$.extend` method.
 */
function extend () {
	var options, name, src, copy, copyIsArray, clone,
		target = arguments[0] || {},
		i = 1,
		length = arguments.length,
		deep = false;

	// Handle a deep copy situation
	if ( typeof target === "boolean" ) {
		deep = target;
		target = arguments[1] || {};
		// skip the boolean and the target
		i = 2;
	}

	// Handle case when target is a string or something (possible in deep copy)
	if ( typeof target !== "object" && !isFunction( target ) ) {
		target = {};
	}

	for ( ; i < length; i++ ) {
		// Only deal with non-null/undefined values
		if ( ( options = arguments[i] ) != null ) {
			// Extend the base object
			for ( name in options ) {
				src = target[ name ];
				copy = options[ name ];

				// Prevent never-ending loop
				if ( target === copy ) {
					continue;
				}

				// Recurse if we're merging plain objects or arrays
				if ( deep && copy && ( isPlainObject( copy ) || ( copyIsArray = isArray( copy ) ) ) ) {
					if ( copyIsArray ) {
						copyIsArray = false;
						clone = src && isArray( src ) ? src : [];
					} else {
						clone = src && isPlainObject( src ) ? src : {};
					}

					// Never move original objects, clone them
					target[ name ] = extend( deep, clone, copy );

				//
				// Don't bring in undefined values
				} else if ( copy !== undefined ) {
					target[ name ] = copy;
				}
			}
		}
	}

	// Return the modified object
	return target;
}
exports.extend = extend;

/**
 * Extracts elements of nested arrays
 */
function flatten ( array ) {
	isArray( array ) || ( array = [ array ] );
	var	i = 0,
		l = array.length,
		item,
		result = [];
	while ( i < l ) {
		item = array[i++];
		isArray( item ) ? ( result = result.concat( flatten( item ) ) ) : result.push( item );
	}
	return result;
}
exports.flatten = flatten;

/**
 * Returns an array containing the keys of a hashmap
 */
function keys ( obj ) {
	var key, result = [];
	if ( !( isPlainObject( obj ) || isFunction( obj ) ) ) { throw new TypeError; }
	for ( key in obj ) hasOwn.call( obj, key ) && result.push( key );
	return result;
}
exports.keys = isFunction( Object.keys ) ? Object.keys || keys;

/**
 * Returns a hashmap that is the key-value inversion of the supplied string array
 */
function invert ( array ) {
	for ( var i = 0, l = array.length, map = {}; i < l; ) {
		map[ array[i] ] = i++;
	}
	return map;
}
exports.invert = invert;

/**
 * Sets all of an object's values to a specified value
 */
function setAll ( obj, value ) {
	for ( var i in obj ) if ( hasOwn.call( obj, i ) ) {
		obj[i] = value;
	}
	return obj;
}
exports.setAll = setAll;

/**
 * Sets all of an object's values to `null`
 */
function nullify ( obj ) {
	for ( var i in obj ) if ( hasOwn.call( obj, i ) ) {
		obj[i] = null;
	}
	return obj;
}
exports.nullify = nullify;

/**
 * Produces a hashmap whose keys are the supplied string array, with values all set to `null`
 */
function nullHash( keys ) { return nullify( invert( keys ) ); }
exports.nullHash = nullHash;

/**
 * Mirrors a function's output as the function's `valueOf`, occasionally useful in debugging/devtools
 */
function valueFunction ( fn ) { return fn.valueOf = fn; }
exports.valueFunction = valueFunction;

/**
 * Mirrors a function's output as the function's `toString`, occasionally useful in debugging/devtools
 */
function stringFunction ( fn ) { return fn.toString = fn; }
exports.stringFunction = stringFunction;

})( typeof module !== 'undefined' ? module.exports : this['Z'] );<commit_msg>v0.1.1<commit_after>( function ( undefined ) {

var	global = this,

	exports = {
		version: '0.1.1',
		env: {
			server: typeof module !== 'undefined' && !!( module.require && module.exports ),
			client: typeof window !== 'undefined' && window === global
		}
	},

	toString = exports.toString =
		Object.prototype.toString,

	hasOwn = exports.hasOwn =
		Object.prototype.hasOwnProperty,

	/** jQuery-style "trim", using native method on `String.prototype` if available. */
	trim = exports.trim =
		String.prototype.trim ?
			function ( text ) { return text == null ? '' : String.prototype.trim.call( text ); } :
			function ( text ) { return text == null ? '' : text.toString().replace( /^\s+/, '' ).replace( /\s+$/, '' ); },

	slice = exports.slice =
		Array.prototype.slice;

exports.noConflict = ( function () {
	var autochthon = global.Z;
	return function () {
		global.Z = autochthon;
		return this;
	};
})();

/**
 * General-purpose empty function. May also be deemed suitable as a unique alternative "nil" type for
 * strict-equal matches whenever it's desirable to avoid traditional `null` and `undefined`.
 */
function noop () {}
exports.noop = noop;

/**
 * Like `noop`, except suited for substitution on methods designed to be chainable.
 */
function getThis () { return this; }
exports.getThis = getThis;

/**
 * Calls the specified native function if it exists and returns its result; if no such function exists on
 * `obj` as registered in `__native.fn`, returns our unique `noop` (as opposed to `null` or `undefined`,
 * which may be a valid result from the native function itself).
 */
function __native ( item, obj /* , ... */ ) {
	var n = __native.fn[item];
	return n && obj[item] === n ? n.apply( obj, slice.call( arguments, 2 ) ) : noop;
}
( exports.__native = __native ).fn = {
	forEach: Array.prototype.forEach
};

/**
 * A more browser-safe alternative to `typeof`, implemented similarly to jQuery and elsewhere, which
 * checks the string output returned by a plain `toString()` call.
 */
function type ( obj ) {
	return obj == null ? String( obj ) : type.map[ toString.call( obj ) ] || 'object';
}
( exports.type = type ).map = {};
each( 'Array Boolean Date Function Number Object RegExp String'.split(' '), function( i, name ) {
	type.map[ "[object " + name + "]" ] = name.toLowerCase();
});

/** isBoolean */
function isBoolean ( obj ) { return type( obj ) === 'boolean'; }
exports.isBoolean = isBoolean;

/** isString */
function isString ( obj ) { return type( obj ) === 'string'; }
exports.isString = isString;

/** isNumber */
function isNumber ( n ) { return !isNaN( parseFloat( n ) ) && isFinite( n ); }
exports.isNumber = isNumber;

/** isArray */
function isArray ( obj ) { return type( obj ) === 'array'; }
exports.isArray = isArray;

/** isFunction */
function isFunction ( obj ) { return type( obj ) === 'function'; }
exports.isFunction = isFunction;

/**
 * Near-straight port of jQuery `isPlainObject`
 */
function isPlainObject ( obj ) {
	var key;
	if ( !obj || type( obj ) !== 'object' || obj.nodeType || obj === global ||
		obj.constructor &&
		!hasOwn.call( obj, 'constructor' ) &&
		!hasOwn.call( obj.constructor.prototype, 'isPrototypeOf' )
	) {
		return false;
	}
	for ( key in obj ) {}
	return key === undefined || hasOwn.call( obj, key );
}
exports.isPlainObject = isPlainObject;

/**
 * Returns a boolean indicating whether the object or array at `obj` contains any members. For an
 * `Object` type, if `andPrototype` is included and truthy, `obj` must be empty throughout its
 * prototype chain as well.
 */
function isEmpty ( obj, andPrototype ) {
	var key;
	if ( isArray( obj ) && obj.length ) {
		return false;
	}
	for ( key in obj ) {
		if ( andPrototype || hasOwn.call( obj, key ) ) {
			return false;
		}
	}
	return true;
}
exports.isEmpty = isEmpty;

/**
 * jQuery-style `$.each`, with callback signature of `key, value, object`.
 */
function each ( obj, fn ) {
	if ( !obj ) { return; }
	var	key, i, l = obj.length;
	if ( l === undefined || isFunction( obj ) ) {
		for ( key in obj ) {
			if ( fn.call( obj[key], key, obj[key], obj ) === false ) {
				break;
			}
		}
	} else {
		for ( i = 0, l = obj.length; i < l; ) {
			if ( fn.call( obj[i], i, obj[i++], obj ) === false ) {
				break;
			}
		}
	}
	return obj;
}
exports.each = each;

/**
 * ES5-style `Array.prototype.forEach`, with callback signature of `value, key, object`.
 */
function forEach ( obj, fn, context ) {
	var	n, l, key, i;
	if ( obj == null ) { return; }
	if ( ( n = __native( 'forEach', obj, fn, context ) ) !== noop ) { return n; }
	if ( ( l = obj.length ) === undefined || isFunction( obj ) ) {
		for ( key in obj ) {
			if ( fn.call( context || obj[key], obj[key], key, obj ) === false ) {
				break;
			}
		}
	} else {
		for ( i = 0, l = obj.length; i < l; ) {
			if ( fn.call( context || obj[i], obj[i], i++, obj ) === false ) {
				break;
			}
		}
	}
	return obj;
}
exports.forEach = forEach;

/**
 * Near-straight port of jQuery `$.extend` method.
 */
function extend () {
	var	flags = {},
		options, name, src, copy, copyIsArray, clone,
		target = arguments[0] || {},
		i = 1,
		length = arguments.length;

	// Handle a deep copy situation
	if ( typeof target === 'string' ) {
		flags = splitToHash( target, ' ' );
		target = arguments[1] || {};
		i = 2;
	}
	else if ( typeof target === 'boolean' ) {
		flags.deep = target;
		target = arguments[1] || {};
		// skip the boolean and the target
		i = 2;
	}

	// Handle case when target is a string or something (possible in deep copy)
	if ( typeof target !== "object" && !isFunction( target ) ) {
		target = {};
	}

	for ( ; i < length; i++ ) {
		// Only deal with non-null/undefined values
		if ( ( options = arguments[i] ) != null || flags.nullable ) {
			// Extend the base object
			for ( name in options ) if ( flags.own || hasOwn.call( options, name ) ) {
				src = target[ name ];
				copy = options[ name ];

				// Prevent never-ending loop
				if ( target === copy ) {
					continue;
				}

				// Recurse if we're merging objects, functions or arrays
				if ( attr.deep && copy && ( isPlainObject( copy ) || ( copyIsArray = isArray( copy ) ) ) ) {
					if ( copyIsArray ) {
						copyIsArray = false;
						clone = src && isArray( src ) ? src : [];
					} else {
						clone = src && ( isFunction( src ) || type( src ) === 'object' ) ? src : {};
					}

					// Never move original objects, clone them
					target[ name ] = extend( keys( flags ).join(' '), clone, copy );

				//
				// Don't bring in undefined values
				} else if ( copy !== undefined || flags.nullable ) {
					target[ name ] = copy;
				}
			}
		}
	}

	// Return the modified object
	return target;
}
exports.extend = extend;

/**
 * Extracts elements of nested arrays
 */
function flatten ( array ) {
	isArray( array ) || ( array = [ array ] );
	var	i = 0,
		l = array.length,
		item,
		result = [];
	while ( i < l ) {
		item = array[i++];
		isArray( item ) ? ( result = result.concat( flatten( item ) ) ) : result.push( item );
	}
	return result;
}
exports.flatten = flatten;

/**
 * Returns an array containing the keys of a hashmap
 */
function keys ( obj ) {
	var key, result = [];
	if ( !( isPlainObject( obj ) || isFunction( obj ) ) ) { throw new TypeError; }
	for ( key in obj ) hasOwn.call( obj, key ) && result.push( key );
	return result;
}
exports.keys = keys = isFunction( Object.keys ) ? Object.keys : keys;

/**
 * Returns a hashmap that is the key-value inversion of the supplied string array
 */
function invert ( array ) {
	for ( var i = 0, l = array.length, map = {}; i < l; ) {
		map[ array[i] ] = i++;
	}
	return map;
}
exports.invert = invert;

/**
 * Sets all of an object's values to a specified value
 */
function setAll ( obj, value ) {
	for ( var i in obj ) if ( hasOwn.call( obj, i ) ) {
		obj[i] = value;
	}
	return obj;
}
exports.setAll = setAll;

/**
 * Sets all of an object's values to `null`
 */
function nullify ( obj ) {
	for ( var i in obj ) if ( hasOwn.call( obj, i ) ) {
		obj[i] = null;
	}
	return obj;
}
exports.nullify = nullify;

/**
 * Returns an object whose keys are the elements of `string.split()` and whose values are all `true`.
 */
function splitToHash ( string, delimiter, value ) {
	return setAll( invert( string.split( delimiter || (/\s+/) ) ), value !== undefined ? value : true );
}
exports.splitToHash = splitToHash;

/**
 * Lazy evaluator; returns a function that returns the enclosed argument
 */
function thunk ( obj ) {
	return function () { return obj; };
}
exports.thunk = thunk;

/**
 * Retrieves the value at the location indicated by the provided `path` string inside a
 * nested object `obj`.
 *
 * E.g.:
 * 	var x = { a: { b: 42 } };
 *  lookup( x, 'a' ) // => { "b": 42 }
 * 	lookup( x, 'a.b' ) // => 42
 * 	lookup( x, 'a.b.c' ) // => undefined
 */
function lookup ( obj, path, separator ) {
	var cursor = obj, i = 0, l = ( path = path.split( separator || '.' ) ).length, name;
	while ( i < l && cursor != null ) {
		if ( hasOwn.call( cursor, name = path[ i++ ] ) ) {
			cursor = cursor[ name ];
		} else {
			return undefined;
		}
	}
	return cursor;
}
exports.lookup = lookup;

/**
 * Reference to or shim for Object.create
 */
function create ( prototype, properties ) {
	var object, constructor;
	( constructor = function () {} ).prototype = prototype;
	( object = new constructor ).constructor = constructor;
	return properties ? extend( object, properties ) : object;
}
exports.create = isFunction( Object.create ) ? ( create = Object.create ) : create;

/**
 * Prototypal inheritance facilitator
 *
 * inherit( Function child, [ Function parent ], [ Object properties ], [ Object statics ] )
 *
 *   * `child` and `parent` are constructor functions, such that
 *         `new child instanceof parent === true`
 *   * `child` also inherits static members that are direct properties of `parent`
 *   * `properties` is an object containing properties to be added to the prototype of `child`
 *   * `statics` is an object containing properties to be added to `child` itself.
 */
function inherit ( child, parent, properties, statics ) {
	isFunction( parent ) ?
		( ( extend( child, parent ).prototype = create( parent.prototype ) ).constructor = child ) :
		( statics = properties, properties = parent );
	properties && extend( child.prototype, properties );
	statics && extend( child, statics );
	return child;
}
exports.inherit = inherit;

/**
 * Returns an object's prototype. In environments without native support, this may only work if
 * the object's constructor and its prototype are properly associated, e.g., as facilitated by
 * the `create` function.
 */
function getPrototypeOf ( obj ) {
	return obj.__proto__ || obj.constructor.prototype;
}
exports.getPrototypeOf = isFunction( Object.getPrototypeOf ) ?
	( getPrototypeOf = Object.getPrototypeOf ) : getPrototypeOf;

/**
 * Produces a hashmap whose keys are the supplied string array, with values all set to `null`
 */
function nullHash( keys ) { return nullify( invert( keys ) ); }
exports.nullHash = nullHash;

/**
 * Mirrors a function's output as the function's `valueOf`, occasionally useful in debugging/devtools
 */
function valueFunction ( fn ) { return fn.valueOf = fn; }
exports.valueFunction = valueFunction;

/**
 * Mirrors a function's output as the function's `toString`, occasionally useful in debugging/devtools
 */
function stringFunction ( fn ) { return fn.toString = fn; }
exports.stringFunction = stringFunction;

exports.env.server && ( module.exports = exports );
exports.env.client && ( global['Z'] = extend( global['Z'] || {}, exports ) );

})();<|endoftext|>


<commit_before>// ==UserScript==
// @name         SG discussions enhanced
// @description  Automatically mark read discussions, show count of new comments since last read, show if post title changed, manually mark one post or all posts of user
// @author       Bladito
// @version      0.6
// @match        https://www.steamgifts.com/discussion*
// @namespace    Bladito/sg-discussions
// @require      http://code.jquery.com/jquery-latest.js
// @grant        GM_addStyle
// ==/UserScript==

(function($) {
    'use strict';

    var storageName = 'Bladito_sg_discussions-read',     // discussions that user already read - saved automatically
        stalkedStorageName = 'Bladito_sg_discussions-stalked-users', // stlaked users (all their discussions will be highlighted) - saved manually
        markedDiscussionsStorageName = 'Bladito_sg_discussions-marked'; // marked discussions (highligted speciffic discussions) - saved manually

    addStyles();

    var discussionMatch = matchDiscussion(location.href);
    var discussionsMatch = location.href.match(/.*\/discussions\/?/);

    if (discussionMatch !== null) {
        rememberReadDiscussion(discussionMatch);
    } else if (discussionsMatch !== null) {
        markReadDiscussions();
    }

    //-------------------------------------------------------------------------------------------------------------------------------------------------

    function matchDiscussion(url) {
        return url.match(/.*\/discussion\/(.*)\/(.*)/);
    }

    function rememberReadDiscussion(discussionMatch) {
        var discussion = {
            name: discussionMatch[2],
            comments: getCommentsCount(),
            time: new Date().getTime()
        };
        addReadDiscussion(discussionMatch[1], discussion);
    }

    function getCommentsCount() {
        return parseNumberFromElement($('.page__heading__breadcrumbs:eq(1)').children());
    }

    function parseNumberFromElement(element) {
        return parseInt(element.text().replace(/[^0-9]/g, ''));
    }

    function markReadDiscussions() {
        var linkDiscussionMatch, $this, $outerWrap, $buttonsWrap, $commentsCountElement, commentsCount, newCommentsCount, newCommentsStyles, originalPoster,
            readDiscussion, readDiscussions = getReadDiscussions(), stalkedUsers = getStalkedUsers(), markedDiscussions = getMarkedDiscussions();

        $('a.table__column__heading').each(function() {
            $this = $(this);
            $outerWrap = $this.closest('.table__row-outer-wrap');
            linkDiscussionMatch = matchDiscussion(this.href);
            readDiscussion = readDiscussions[linkDiscussionMatch[1]];

            if (readDiscussion) { //we have read this discussion already
                $outerWrap.css({
                    opacity: 0.3,
                    margin: '0 -20px 0 20px'
                });
                if (readDiscussion.name !== linkDiscussionMatch[2]) { //OP changed discussion name
                    $this.css('color', '#fd00ff');
                    $this.attr('title', 'Changed from: "' + readDiscussion.name + '"');
                }
                $commentsCountElement = $outerWrap.find('.table__column--width-small .table__column__secondary-link');
                commentsCount = parseNumberFromElement($commentsCountElement);
                if (readDiscussion.comments !== commentsCount) {
                    newCommentsCount = commentsCount - readDiscussion.comments;
                    newCommentsStyles = getStylesForNewComments(newCommentsCount);
                    $commentsCountElement.after('(+'+newCommentsCount+')');
                }
            }
            originalPoster = $outerWrap.find('.table__column--width-fill .table__column__secondary-link:eq(1)').text();

            $buttonsWrap = $('

');
            $outerWrap.prepend($buttonsWrap);

            if (stalkedUsers.indexOf(originalPoster) > -1) {
                markPostOfStalkedUser($outerWrap);
                addUnstalkButton($buttonsWrap, $outerWrap, originalPoster);
            } else {
                addStalkButton($buttonsWrap, $outerWrap, originalPoster);
            }

            if (markedDiscussions.indexOf(linkDiscussionMatch[1]) > -1) {
                markDiscussion($outerWrap);
                addUnmarkButton($buttonsWrap, $outerWrap, linkDiscussionMatch[1]);
            } else {
                addMarkButton($buttonsWrap, $outerWrap, linkDiscussionMatch[1]);
            }
        });
    }

    function addStalkButton($buttonsWrap, $outerWrap, originalPoster) {
        var stalkButton = $('<button class="bsg-action-btn" title="stalk = every post of this user will be highlighted"></button>
');
        $buttonsWrap.append(stalkButton);
        stalkButton.on('click', function() {
            addOrRemoveStalkedUser(originalPoster,$outerWrap);
            stalkButton.remove();
            addUnstalkButton($buttonsWrap, $outerWrap, originalPoster);
        });
    }
    function addUnstalkButton($buttonsWrap, $outerWrap, originalPoster) {
        var unstalkButton = $('<button class="bsg-action-btn m-active" title="unstalk = every post of this user will not be highlighted anymore"></button>
');
        $buttonsWrap.append(unstalkButton);
        unstalkButton.on('click', function() {
            addOrRemoveStalkedUser(originalPoster,$outerWrap);
            unstalkButton.remove();
            addStalkButton($buttonsWrap, $outerWrap, originalPoster);
        });
    }

    function addMarkButton($buttonsWrap, $outerWrap, discussionId) {
        var markButton = $('<button class="bsg-action-btn" title="mark = highlight this post"></button>');
        $buttonsWrap.prepend(markButton);
        markButton.on('click', function() {
            addMarkedDiscussion(discussionId,$outerWrap);
            markButton.remove();
            addUnmarkButton($buttonsWrap, $outerWrap, discussionId);
        });
    }
    function addUnmarkButton($buttonsWrap, $outerWrap, discussionId) {
        var unmarkButton = $('<button class="bsg-action-btn m-active" title="unmark = remove highlight of this post"></button>');
        $buttonsWrap.prepend(unmarkButton);
        unmarkButton.on('click', function() {
            addMarkedDiscussion(discussionId,$outerWrap);
            unmarkButton.remove();
            addMarkButton($buttonsWrap, $outerWrap, discussionId);
        });
    }

    function markPostOfStalkedUser($outerWrap) {
        $outerWrap.css('border', '2px solid red');
    }
    function unmarkPostOfStalkedUser($outerWrap) {
        $outerWrap.css('border', '');
    }

    function markDiscussion($outerWrap) {
        $outerWrap.css('border', '2px dashed green');
    }
    function unmarkDiscussion($outerWrap) {
        $outerWrap.css('border', '');
    }

    function getStylesForNewComments(commentsCount) {
        var styles = {number: '', section: ''};
        if (commentsCount >= 100) {
            styles.section = 'color: #fd00ff; margin-left: 5px;';
            styles.number = 'font-size: 25px; font-weight: 900;';
        } else if (commentsCount >= 50) {
            styles.section = 'color: red; margin-left: 5px;';
            styles.number = 'font-size: 20px; font-weight: 600;';
        } else if (commentsCount >= 10) {
            styles.section = 'color: blue; margin-left: 5px;';
            styles.number = 'font-size: 13px;';
        }
        return styles;
    }

    function addReadDiscussion(id, discussion) {
        var readDiscussions = getReadDiscussions() || {};
        readDiscussions[id] = discussion;
        localStorage.setItem(storageName, JSON.stringify(readDiscussions));
    }

    function getReadDiscussions() {
        return JSON.parse(localStorage.getItem(storageName)) || {};
    }

    function getStalkedUsers() {
        return JSON.parse(localStorage.getItem(stalkedStorageName)) || [];
    }

    function getMarkedDiscussions() {
        return JSON.parse(localStorage.getItem(markedDiscussionsStorageName)) || [];
    }

    function addOrRemoveStalkedUser(userName,$outerWrap) {
        var users = getStalkedUsers(),
            index = users.indexOf(userName);
        if (index < 0) {
            users.push(userName);
            localStorage.setItem(stalkedStorageName, JSON.stringify(users));
            markPostOfStalkedUser($outerWrap);
        } else {
            users.splice(index, 1);
            localStorage.setItem(stalkedStorageName, JSON.stringify(users));
            unmarkPostOfStalkedUser($outerWrap);
        }
    }

    function addMarkedDiscussion(discussionId,$outerWrap) {
        var markedDiscussions = getMarkedDiscussions(),
            index = markedDiscussions.indexOf(discussionId);
        if (index < 0) {
            markedDiscussions.push(discussionId);
            localStorage.setItem(markedDiscussionsStorageName, JSON.stringify(markedDiscussions));
            markDiscussion($outerWrap);
        } else {
            markedDiscussions.splice(index, 1);
            localStorage.setItem(markedDiscussionsStorageName, JSON.stringify(markedDiscussions));
            unmarkDiscussion($outerWrap);
        }
    }

    function addStyles() {
        GM_addStyle('.action-btns-wrap { display: none; position: absolute; margin-left: -115px; width: 115px; }');
        GM_addStyle('.table__row-outer-wrap:hover > .action-btns-wrap { display: block; }');
        GM_addStyle('.bsg-action-btn { ' +
                    'display: inline-block; margin: 5px; ' +
                    'border: 1px solid #d6d6d6;' +
                    'font: 700 13px/32px "Open Sans",sans-serif;' +
                    'padding: 0 15px;' +
                    'cursor: pointer;' +
                    'text-align: center;' +
                    'border-radius: 4px;' +
                    'background-color: #f3f3f3;' +
                    'background-image: none;' +
                    'color: #757575;' +
                    'width: 46px;' +
                    '}');
        GM_addStyle('.bsg-action-btn.m-active, .bsg-action-btn:hover {' +
                    'border-color: #B9D393 #96BC69 #73A442 #A0C870;' +
                    'background-image: linear-gradient(#cef2aa 0%, #b4e08a 50%, #9AC96A 100%);' +
                    'background-image: -moz-linear-gradient(#cef2aa 0%, #b4e08a 50%, #9AC96A 100%);' +
                    'background-image: -webkit-linear-gradient(#cef2aa 0%, #b4e08a 50%, #9AC96A 100%);' +
                    '}');
        GM_addStyle('.bsg-action-btn.m-active:hover {' +
                    'border-color: #edbecd #f0afc5 #de95ae #f2b1c8 !important;' +
                    'background-image: linear-gradient(#fad9e4 0%, #f1c0d2 100%);' +
                    'background-image: -moz-linear-gradient(#fad9e4 0%, #f1c0d2 100%);' +
                    'background-image: -webkit-linear-gradient(#fad9e4 0%, #f1c0d2 100%);' +
                    '}');
    }

})(jQuery);
<commit_msg>removed indentation of read discussions and fade effect of marked discussions + changed all styles to classes<commit_after>// ==UserScript==
// @name         SG discussions enhanced
// @description  Automatically mark read discussions, show count of new comments since last read, show if post title changed, manually mark one post or all posts of user
// @author       Bladito
// @version      0.7
// @match        https://www.steamgifts.com/discussion*
// @namespace    Bladito/sg-discussions
// @require      http://code.jquery.com/jquery-latest.js
// @grant        GM_addStyle
// ==/UserScript==

(function($) {
    'use strict';

    var storageName = 'Bladito_sg_discussions-read',     // discussions that user already read - saved automatically
        stalkedStorageName = 'Bladito_sg_discussions-stalked-users', // stlaked users (all their discussions will be highlighted) - saved manually
        markedDiscussionsStorageName = 'Bladito_sg_discussions-marked'; // marked discussions (highligted speciffic discussions) - saved manually

    addStyles();

    var discussionMatch = matchDiscussion(location.href);
    var discussionsMatch = location.href.match(/.*\/discussions\/?/);

    if (discussionMatch !== null) {
        rememberReadDiscussion(discussionMatch);
    } else if (discussionsMatch !== null) {
        markReadDiscussions();
    }

    //-------------------------------------------------------------------------------------------------------------------------------------------------

    function matchDiscussion(url) {
        return url.match(/.*\/discussion\/(.*)\/(.*)/);
    }

    function rememberReadDiscussion(discussionMatch) {
        var discussion = {
            name: discussionMatch[2],
            comments: getCommentsCount(),
            time: new Date().getTime()
        };
        addReadDiscussion(discussionMatch[1], discussion);
    }

    function getCommentsCount() {
        return parseNumberFromElement($('.page__heading__breadcrumbs:eq(1)').children());
    }

    function parseNumberFromElement(element) {
        return parseInt(element.text().replace(/[^0-9]/g, ''));
    }

    function markReadDiscussions() {
        var linkDiscussionMatch, $this, $outerWrap, $buttonsWrap, $commentsCountElement, commentsCount, newCommentsCount, originalPoster,
            readDiscussion, readDiscussions = getReadDiscussions(), stalkedUsers = getStalkedUsers(), markedDiscussions = getMarkedDiscussions();

        $('a.table__column__heading').each(function() {
            $this = $(this);
            $outerWrap = $this.closest('.table__row-outer-wrap');
            linkDiscussionMatch = matchDiscussion(this.href);
            readDiscussion = readDiscussions[linkDiscussionMatch[1]];

            if (readDiscussion) { //we have read this discussion already
                $outerWrap.addClass('bsg-discussion-read');
                if (readDiscussion.name !== linkDiscussionMatch[2]) { //OP changed discussion name
                    $this.addClass('bsg-discussion-title-changed');
                    $this.attr('title', 'Changed from: "' + readDiscussion.name + '"');
                }
                $commentsCountElement = $outerWrap.find('.table__column--width-small .table__column__secondary-link');
                commentsCount = parseNumberFromElement($commentsCountElement);
                if (readDiscussion.comments !== commentsCount) {
                    newCommentsCount = commentsCount - readDiscussion.comments;
                    $commentsCountElement.after('(+'+newCommentsCount+')');
                }
            }
            originalPoster = $outerWrap.find('.table__column--width-fill .table__column__secondary-link:eq(1)').text();

            $buttonsWrap = $('

');
            $outerWrap.prepend($buttonsWrap);

            if (stalkedUsers.indexOf(originalPoster) > -1) {
                markPostOfStalkedUser($outerWrap);
                addUnstalkButton($buttonsWrap, $outerWrap, originalPoster);
            } else {
                addStalkButton($buttonsWrap, $outerWrap, originalPoster);
            }

            if (markedDiscussions.indexOf(linkDiscussionMatch[1]) > -1) {
                markDiscussion($outerWrap);
                addUnmarkButton($buttonsWrap, $outerWrap, linkDiscussionMatch[1]);
            } else {
                addMarkButton($buttonsWrap, $outerWrap, linkDiscussionMatch[1]);
            }
        });
    }

    function addStalkButton($buttonsWrap, $outerWrap, originalPoster) {
        var stalkButton = $('<button class="bsg-action-btn" title="stalk = every post of this user will be highlighted"></button>
');
        $buttonsWrap.append(stalkButton);
        stalkButton.on('click', function() {
            addOrRemoveStalkedUser(originalPoster,$outerWrap);
            stalkButton.remove();
            addUnstalkButton($buttonsWrap, $outerWrap, originalPoster);
        });
    }
    function addUnstalkButton($buttonsWrap, $outerWrap, originalPoster) {
        var unstalkButton = $('<button class="bsg-action-btn m-active" title="unstalk = every post of this user will not be highlighted anymore"></button>
');
        $buttonsWrap.append(unstalkButton);
        unstalkButton.on('click', function() {
            addOrRemoveStalkedUser(originalPoster,$outerWrap);
            unstalkButton.remove();
            addStalkButton($buttonsWrap, $outerWrap, originalPoster);
        });
    }

    function addMarkButton($buttonsWrap, $outerWrap, discussionId) {
        var markButton = $('<button class="bsg-action-btn" title="mark = highlight this post"></button>');
        $buttonsWrap.prepend(markButton);
        markButton.on('click', function() {
            addMarkedDiscussion(discussionId,$outerWrap);
            markButton.remove();
            addUnmarkButton($buttonsWrap, $outerWrap, discussionId);
        });
    }
    function addUnmarkButton($buttonsWrap, $outerWrap, discussionId) {
        var unmarkButton = $('<button class="bsg-action-btn m-active" title="unmark = remove highlight of this post"></button>');
        $buttonsWrap.prepend(unmarkButton);
        unmarkButton.on('click', function() {
            addMarkedDiscussion(discussionId,$outerWrap);
            unmarkButton.remove();
            addMarkButton($buttonsWrap, $outerWrap, discussionId);
        });
    }

    function markPostOfStalkedUser($outerWrap) {
        $outerWrap.addClass('bsg-stalked-discussion');
    }
    function unmarkPostOfStalkedUser($outerWrap) {
        $outerWrap.removeClass('bsg-stalked-discussion');
    }

    function markDiscussion($outerWrap) {
        $outerWrap.addClass('bsg-marked-discussion');
    }
    function unmarkDiscussion($outerWrap) {
        $outerWrap.removeClass('bsg-marked-discussion');
    }

    function getClassesForNewComments(commentsCount) {
        var classes = 'bsg-new-comments ';
        if (commentsCount >= 100) {
            classes += 'm-high';
        } else if (commentsCount >= 50) {
            classes += 'm-medium';
        } else if (commentsCount >= 10) {
            classes += 'm-low';
        }
        return classes;
    }

    function addReadDiscussion(id, discussion) {
        var readDiscussions = getReadDiscussions() || {};
        readDiscussions[id] = discussion;
        localStorage.setItem(storageName, JSON.stringify(readDiscussions));
    }

    function getReadDiscussions() {
        return JSON.parse(localStorage.getItem(storageName)) || {};
    }

    function getStalkedUsers() {
        return JSON.parse(localStorage.getItem(stalkedStorageName)) || [];
    }

    function getMarkedDiscussions() {
        return JSON.parse(localStorage.getItem(markedDiscussionsStorageName)) || [];
    }

    function addOrRemoveStalkedUser(userName,$outerWrap) {
        var users = getStalkedUsers(),
            index = users.indexOf(userName);
        if (index < 0) {
            users.push(userName);
            localStorage.setItem(stalkedStorageName, JSON.stringify(users));
            markPostOfStalkedUser($outerWrap);
        } else {
            users.splice(index, 1);
            localStorage.setItem(stalkedStorageName, JSON.stringify(users));
            unmarkPostOfStalkedUser($outerWrap);
        }
    }

    function addMarkedDiscussion(discussionId,$outerWrap) {
        var markedDiscussions = getMarkedDiscussions(),
            index = markedDiscussions.indexOf(discussionId);
        if (index < 0) {
            markedDiscussions.push(discussionId);
            localStorage.setItem(markedDiscussionsStorageName, JSON.stringify(markedDiscussions));
            markDiscussion($outerWrap);
        } else {
            markedDiscussions.splice(index, 1);
            localStorage.setItem(markedDiscussionsStorageName, JSON.stringify(markedDiscussions));
            unmarkDiscussion($outerWrap);
        }
    }

    function addStyles() {
        GM_addStyle('.action-btns-wrap { display: none; position: absolute; margin-left: -115px; width: 115px; }');
        GM_addStyle('.table__row-outer-wrap:hover > .action-btns-wrap { display: block; }');
        GM_addStyle('.bsg-action-btn { ' +
                    'display: inline-block; margin: 5px; ' +
                    'border: 1px solid #d6d6d6;' +
                    'font: 700 13px/32px "Open Sans",sans-serif;' +
                    'padding: 0 15px;' +
                    'cursor: pointer;' +
                    'text-align: center;' +
                    'border-radius: 4px;' +
                    'background-color: #f3f3f3;' +
                    'background-image: none;' +
                    'color: #757575;' +
                    'width: 46px;' +
                    '}');
        GM_addStyle('.bsg-action-btn.m-active, .bsg-action-btn:hover {' +
                    'border-color: #B9D393 #96BC69 #73A442 #A0C870;' +
                    'background-image: linear-gradient(#cef2aa 0%, #b4e08a 50%, #9AC96A 100%);' +
                    'background-image: -moz-linear-gradient(#cef2aa 0%, #b4e08a 50%, #9AC96A 100%);' +
                    'background-image: -webkit-linear-gradient(#cef2aa 0%, #b4e08a 50%, #9AC96A 100%);' +
                    '}');
        GM_addStyle('.bsg-action-btn.m-active:hover {' +
                    'border-color: #edbecd #f0afc5 #de95ae #f2b1c8 !important;' +
                    'background-image: linear-gradient(#fad9e4 0%, #f1c0d2 100%);' +
                    'background-image: -moz-linear-gradient(#fad9e4 0%, #f1c0d2 100%);' +
                    'background-image: -webkit-linear-gradient(#fad9e4 0%, #f1c0d2 100%);' +
                    '}');

        GM_addStyle('.bsg-discussion-read.table__row-outer-wrap {' +
                    'opacity: 0.3;' +
                    '}');
        GM_addStyle('.bsg-stalked-discussion.table__row-outer-wrap {' +
                    'border: 2px solid red;' +
                    'opacity: 1;' +
                    '}');
        GM_addStyle('.bsg-marked-discussion.table__row-outer-wrap {' +
                    'border: 2px dashed green;' +
                    'opacity: 1;' +
                    '}');
        GM_addStyle('.bsg-discussion-title-changed {' +
                    'color: #fd00ff;' +
                    '}');

        GM_addStyle('.bsg-new-comments {' +
                    'margin-left: 5px;' +
                    '}');
        GM_addStyle('.bsg-new-comments.m-high {' +
                    'color: #fd00ff;' +
                    'font-size: 25px; font-weight: 900;' +
                    '}');
        GM_addStyle('.bsg-new-comments.m-medium {' +
                    'color: red;' +
                    'font-size: 20px; font-weight: 600;' +
                    '}');
        GM_addStyle('.bsg-new-comments.m-low {' +
                    'color: blue;' +
                    'font-size: 13px;' +
                    '}');
    }

})(jQuery);
<|endoftext|>


<commit_before>(function(env) {
    "use strict";

    env.ddg_spice_anime = function(api_result) {

        if (!api_result || api_result.length === 0) {
            return Spice.failed("anime");
        }

        var script = $('[src*="/js/spice/anime/"]')[0],
        source = $(script).attr("src"),
        query = source.match(/anime\/([^\/]+)/)[1],
        decodedQuery = decodeURIComponent(query);

        Spice.add({
            id: 'anime',
            name: 'Anime',
            data: api_result,
            meta: {
                sourceName: 'Hummingbird',
                sourceUrl: "https://hummingbird.me/search?query=" + query,
                sourceIconUrl: DDG.get_asset_path('anime','hummingbird.png'),
                itemType: 'Anime'
            },
            normalize: function(item) {
                var cover_image = '';
                if (item.cover_image) {
                    cover_image = item.cover_image.replace('https', 'http');
                }

                return {
                    img: cover_image,
                    image: cover_image,
                    img_m: cover_image,
                    heading: item.title,
                    title: item.title,
                    abstract: item.synopsis.split(' ').slice(0,49).join(' ') + '...',
                    rating: item.community_rating,
                    url: item.url,
                    is_retina: (DDG.is3x || DDG.is2x) ? "is_retina" : "no_retina"
                };
            },
            templates: {
                group: 'movies',
                options: {
                    subtitle_content: Spice.anime.subtitle_content,
                    rating: true,
                    buy: Spice.anime.buy
                }
            }
        });
    };
}(this));

<commit_msg>anime- using toHTTP instead of string replacement<commit_after>(function(env) {
    "use strict";

    env.ddg_spice_anime = function(api_result) {

        if (!api_result || api_result.length === 0) {
            return Spice.failed("anime");
        }

        var script = $('[src*="/js/spice/anime/"]')[0],
        source = $(script).attr("src"),
        query = source.match(/anime\/([^\/]+)/)[1],
        decodedQuery = decodeURIComponent(query);

        Spice.add({
            id: 'anime',
            name: 'Anime',
            data: api_result,
            meta: {
                sourceName: 'Hummingbird',
                sourceUrl: "https://hummingbird.me/search?query=" + query,
                sourceIconUrl: DDG.get_asset_path('anime','hummingbird.png'),
                itemType: 'Anime'
            },
            normalize: function(item) {
                var cover_image = '';
                if (item.cover_image) {
                    cover_image = DDG.toHTTP(item.cover_image);
                }

                return {
                    img: cover_image,
                    image: cover_image,
                    img_m: cover_image,
                    heading: item.title,
                    title: item.title,
                    abstract: item.synopsis.split(' ').slice(0,49).join(' ') + '...',
                    rating: item.community_rating,
                    url: item.url,
                    is_retina: (DDG.is3x || DDG.is2x) ? "is_retina" : "no_retina"
                };
            },
            templates: {
                group: 'movies',
                options: {
                    subtitle_content: Spice.anime.subtitle_content,
                    rating: true,
                    buy: Spice.anime.buy
                }
            }
        });
    };
}(this));

<|endoftext|>


<commit_before>function ddg_spice_bitly (r) {
	if (r['status_code'] == 200) {
		url = r['data']['url'];
		items = new Array();
		items[0] = new Array();
		items[0]['a'] = '
Shortened URL: ' + url + '
';
		items[0]['h'] = 'Shortened URL (bitly)';
		items[0]['s'] = 'Bitly';
		items[0]['u'] = 'http://bitly.com';
		nra(items);
	}
}
<commit_msg>minor style tweaks, added bitly image.<commit_after>function ddg_spice_bitly (r) {
	if (r['status_code'] == 200) {
		url = r['data']['url'];
		items = new Array();
		items[0] = new Array();
		items[0]['a'] = 'Here is a shortened version of that URL: ' + url + '
';
		items[0]['h'] = 'Shortened URL (bitly)';
		items[0]['i'] = 'http://i.imgur.com/SPtMJ.png';
		items[0]['s'] = 'Bitly';
		items[0]['u'] = 'http://bitly.com';
		items[0]['force_big_header'] = true;
		nra(items);
	}
}
<|endoftext|>


<commit_before>function ddg_spice_movie(movie) {
      // console.log(xk);

      var result,img,snippet,link,div;

      // validity check
      if (movie['total'] > 0 && movie['movies']) {

        result = movie["movies"][0];

        // Create snippet to be shown
        snippet = '';

        // Check presence of synopsis, and create element
        if (result.synopsis.length > 0) synopsis = result.synopsis.substring(0,140) + "...";
        else if (result.critics_consensus && result.critics_consensus.length > 0) synopsis = result.critics_consensus.substring(0,140) + "...";
        else synopsis = '';

        var names = [];
        // Loop through abridged cast members, add to cast element
        for (var i=0; i < result.abridged_cast.length; i++){
          var pre = '';
          if ( i == result.abridged_cast.length - 1 && result.abridged_cast.length != 1 ) pre = 'and ';
          var name = result.abridged_cast[i].name;
          var url = 'http://www.rottentomatoes.com/celebrity/' + result.abridged_cast[i].id + '/';

          names.push(pre+''+name+'');
        }
        var cast = '';
        if (names.length > 1) cast = ', starring '+names.join(', ');

        // check for default poster
        var poster = result.posters.thumbnail;
        if (poster === 'http://images.rottentomatoescdn.com/images/redesign/poster_default.gif') poster = '';

        // Make title for header
        var header = result.title.substring(0,49);
        if (result.title.length > 50) header += '...';

        //Movie Score
        var score = 'with an audience score of'+result.audience_score+'%'

        // Call nra function as per Spice Plugin Guidelines
        items = new Array();
        items[0] = new Array();
        items[0]['a'] = result.title + ' is a '+result.year+ ' movie ('
                        +result.mpaa_rating+ ', '
                        +result.ratings.audience_score+ '%, '
                        +result.ratings.critics_score+ '% critic approved)'
                        +cast+ '. '
                        +synopsis;

        items[0]['h'] = header;

        // Source name and url for the More at X link.
        items[0]['s'] = 'Rotten Tomatoes';
        items[0]['u'] = result.links.alternate;

        // Force no compression.
        items[0]['f'] = 1;

        // Thumbnail url
        items[0]['i'] = poster;

        // The rendering function is nra.
        nra(items);
     }
    }
<commit_msg>removed local isRelevant function, fixed code indentation, need to use new isRelevant function<commit_after>var req = document.getElementById('search_form_input').value;
var skipArray = new Array();
skipArray['movie'] = 1;
skipArray['film']  = 1;

function ddg_spice_movie(movie) {
    console.log(movie);
    console.log('query is: '+req);
    var result = req.replace(/^movie\s+/g,'');
    console.log('scrubbed query is: '+result);

    // validity check
    if (movie['total'] > 0 && movie['movies']) {
        console.log('got movies');

        // more than one result
        if (movie['total'] > 1) {
            console.log('find the right one.');
            // movie.movies.foreach(DDG.isRelevant( this.title, ['movie'] )
            var relevant = new RegExp(result, 'ig');

            // Default to first result incase nothing is more relevant
            result = movie["movies"][0];

            // check which movie title is most relevant
            for (var i = 0, aMovie; aMovie = movie.movies[i]; i++){
                if (isRelevant(aMovie.title, skipArray) ) {
                    console.log('relevant movie!');
                    console.log(aMovie.title);
                    result = aMovie;
                    break;
                }
            }
        } else {
            console.log('DEFAULT MOVIE!');
            result = movie["movies"][0];
        }

        // Create snippet to be shown
        snippet = '';

        console.log('result is: ' +result);
        // Check presence of synopsis, and create element

        if (result.synopsis) synopsis = result.synopsis.substring(0,140) + "...";
        else if (result.critics_consensus && result.critics_consensus.length > 0) synopsis = result.critics_consensus.substring(0,140) + "...";
        else synopsis = '';

        synopsis = '';

        var names = [];
        // Loop through abridged cast members, add to cast element
        for (var i=0; i < result.abridged_cast.length; i++){
            var pre = '';
            if ( i == result.abridged_cast.length - 1 && result.abridged_cast.length != 1 ) pre = 'and ';
            var name = result.abridged_cast[i].name;
            var url = 'http://www.rottentomatoes.com/celebrity/' + result.abridged_cast[i].id + '/';

            names.push(pre+''+name+'');
        }

        var cast = '';
        if (names.length > 1) cast = ', starring '+names.join(', ');

        // check for default poster
        var poster = result.posters.thumbnail;
        if (poster === 'http://images.rottentomatoescdn.com/images/redesign/poster_default.gif') poster = '';

        // Make title for header
        var header = result.title.substring(0,49);
        if (result.title.length > 50) header += '...';

        //Movie Score
        var score = 'with an audience score of'+result.audience_score+'%'

        // Call nra function as per Spice Plugin Guidelines
        items = new Array();
        items[0] = new Array();
        items[0]['a'] = result.title + ' is a '+result.year+ ' movie ('
                        +result.mpaa_rating+ ', '
                        +result.ratings.audience_score+ '%, '
                        +result.ratings.critics_score+ '% critic approved)'
                        +cast + '. '
                        +synopsis;

        items[0]['h'] = header;

        // Source name and url for the More at X link.
        items[0]['s'] = 'Rotten Tomatoes';
        items[0]['u'] = result.links.alternate;

        // Force no compression.
        items[0]['f'] = 1;

        // Thumbnail url
        items[0]['i'] = poster;

        // The rendering function is nra.
        nra(items);
    }
}<|endoftext|>