<commit_before>'use strict';

const _ = require('lodash');
const Script = require('smooch-bot').Script;
var pg = require('pg');
var Q = require("q");
var request = require("request");
var newUser = require("./db");
var logConversation = require("./conversation");
var nlp = require("./nlp");
var pushConv = require("./push");
var newBot_msg = require("./newBot");
//var findSession = require("./sessionsearch");

//var sched = require("./sched");

const scriptRules = require('./script.json');

var msgLog = {
    smoochId: '',
    received: '',
    usermessage: '',
    role: '',
    message_id: '',
    sourcetype: '',
    receivedtime: '',
    responsemessage: '',
    responsetype: '',
    responsetime: '',
    newUsercheck: 'false'
  };

function wait(ms) {
    return new Promise((resolve) => {
        setTimeout(resolve, ms);
    });
}

module.exports = new Script({
    processing: {
        //prompt: (bot) => bot.say('Beep boop...'),
        receive: () => 'processing'
    },

    start: {
        receive: (bot) => {
            return bot.say('Hello!  I\'m A16, the Agile2016 EventBot Concierge.  To find out what I can help you with type MENU for options or KEY for Keywords.')
                .then(() => 'speak');
        }
    },

//    s1: {
//      prompt: (bot) => bot.say('Type part of the session name?'),
//      receive: (bot, message) => {
//        const name = message.text;
//        return bot.setProp('name', name)
//            .then(() => bot.say(`Great! I'll call you ${name}
//Is that OK? %[Yes](postback:yes) %[No](postback:no)`))
//            .then(() => 'speak');
//      }
//    },

//    chris: {
//      receive: (bot, message) => {
//        return bot.getProp('name')
//          .then((name) => bot.say('That is all!'))
//          .then(() => 'speak');
//      }
//    },

    speak: {
        receive: (bot, message) => {

            console.log("===bot message ", message);

            let upperText = message.text.trim().toUpperCase();

//TODO: don't try stateMachine; instead capture last text and if a search thing ie is search by speaker
//then the submit is speaker name and call speakersearch won't work for is session expires
//            if (msgLog.usermessage == 'SBSN') {
//                console.log("=== search string", upperText);
//                var msg = {};
//                findSession(upperText, msg);
//                return bot.setProp("silent", true);
//            }

            msgLog.smoochId = bot.userId;
            msgLog.received = message.received;
            msgLog.usermessage = message.text;
            msgLog.role = message.role;
            msgLog.message_id = message._id;
            msgLog.receivedtime = new Date();
//this mess is my way around the fact that smooch completely  changes the structure of the message obj if it is a postback vs user entered text
//            console.log("===message.message",message.message);
            switch (typeof message.message === "undefined") {
              case false:
//                  console.log("!!!! appMaker = T, message.role", message.message);
                  msgLog.sourcetype = message.action.type;
                break;
              default:
//                  console.log("!!!! appUser = T, message.role", message.source);
                  msgLog.sourcetype = message.source.type;
                break;
            }

            //SK_ACCESS is a heroku config var that has the list of devices smoochids for auth users to send ad hoc push conversations
            var authUsers = process.env.SK_ACCESS

            //Not sure if this is the best way to accomplish not calling newUser everytime, but it seems to work
            if(msgLog.newUsercheck == 'false') {
              console.log("===NewUser");
              newUser(bot)
                .then(msgLog.newUsercheck = 'true');
            }

            //For ad hoc messages - scheduled messages are done differently in checkItems
            if (authUsers.indexOf(bot.userId) !== -1) {
              if (upperText.substr(0,4) == '/SK ') {
                upperText = upperText.substr(0,3);
                newBot_msg(message.text.substr(4));
                console.log("****after push msg:  ",message.text," authUser:  ",authUsers);
              }
            }

//TODO: There is a bug with /support and /a16 now.  /support does not present message and /a16 sometimes doubles the message
            function updateSilent() {
                switch (upperText) {
                    case "CONNECT ME":
                        return bot.setProp("silent", true);
                    case "/SUPPORT":
                        console.log("*** /support", upperText);
//                        processMessage(false);
                        return bot.setProp("silent", true);
                    case "DISCONNECT":
                        return bot.setProp("silent", false);
                    case "/A16":
                        console.log("*** /A16 ",upperText," ***");
                        processMessage(false);
                        return bot.setProp("silent", false);
                    default:
                        return Promise.resolve();
                }
            }

            function getSilent() {
                return bot.getProp("silent");
            }

            function processMessage(isSilent) {
                if (isSilent) {
                    return Promise.resolve("speak");
                }

                var promises = [];
                var source;
                var fulfillmentSpeech;
                var simplified;
                promises.push(nlp(upperText, bot.userId));

                Q.all(promises).then(function(responses) {
                    // response is the JSON from API.ai
                    responses.forEach(function(response) {
                        console.log("===in Q.all");
                        console.log("===received result from API.ai",response);
                        source = response.result.source;
                        if (source && source !== 'agent')
                        {
                            console.log("====Q all not agent");
                            fulfillmentSpeech = response.result.fulfillment.speech;
                            simplified = response.result.parameters.simplified;
                        } else if (source && source == 'agent') {
                          console.log("=== source is agent ", response.result);
                          fulfillmentSpeech = response.result.fulfillment.speech;
                          simplified = response.result.metadata.parameters.event;
                        }
                        console.log("source: ", source);
                        console.log("fulfillmentSpeech: ", fulfillmentSpeech);
                        console.log("simplified: ", simplified);

                        respondMessage(source, fulfillmentSpeech, simplified);
                    });
                }, function(error) {
                    console.log("===Q all error ", error);
                });
            }

            function respondMessage(source, fulfillmentSpeech, simplified)
            {
                console.log("source: ", source);
                console.log("fulfillmentSpeech: ", fulfillmentSpeech);
                console.log("simplified: ", simplified);
                console.log("===receive step 3",upperText);

              //if (source != 'agent')
              console.log("?????? what is the source:  ", source);
              switch (source) {
                case 'domains':
                    console.log("===source is ", source);
                    if (fulfillmentSpeech)
                    {
                      switch (simplified) {
                        case "hello":
                          console.log("===in hello");
                          upperText = simplified.trim().toUpperCase();
                          break;
                        case "do you know":
                        case "can you help":
                        case "do you have":
                        case "how does this app work":
                        case "how much time do you need":
                        case "how to open you":
                        case "what can you talk about":
                        case "what do you know":
                          console.log("===in what do you know");
                          upperText = 'KNOW';
                          break;
                        case "what do you do":
                        case "how do you know":
                        case "job":
                          console.log("===in what do do");
                          upperText = "JOB";
                          break;
                        case "do you know me":
                        case "do you remember me":
                          console.log("===do you know me");
                          upperText = "ME";
                          break;
                        case "who named you":
                        console.log("===NAME");
                          upperText = "NAME";
                          break;
                        case "can you hear me":
                        case "can you speak":
                        case "change your":
                        case "hurry":
                        case "talk faster":
                        case "do you drink":
                        case "do you eat":
                          console.log("===set to NULL and question");
                          upperText = "";
                          break;
                        default:
                          console.log("===in switch default");
                          if (upperText == "EVENING") {break;}
                          msgLog.responsemessage = fulfillmentSpeech;
                          msgLog.responsetime = new Date;
                          msgLog.responsetype = 'API.AI Domain';
                          return bot.say(fulfillmentSpeech).then(() => 'speak');
                      }
                    }
                    break;
                  case 'agent':
                    if (simplified == 'agile2017')
                      {
                          console.log("simplified is: ", simplified);
                          msgLog.responsemessage = fulfillmentSpeech;
                          msgLog.responsetime = new Date;
                          msgLog.responsetype = 'API.AI Intent';
                          return bot.say(fulfillmentSpeech).then(() => 'speak');
                      }
                    break;
                  default:
                    console.log("===finished switch, upperText now:",upperText);
                  }

                if (!_.has(scriptRules, upperText)) {
                    console.log("===no rule", upperText);
                    return bot.say(`I'm sorry that is not something I know.  Type MENU or KEY for a list of things I can help you with.`).then(() => 'speak');
                }

                var response = scriptRules[upperText];
                var lines = response.split('\n');
                msgLog.responsemessage = response;
                msgLog.responsetime = new Date;
                msgLog.responsetype = 'JSON';
                console.log("=== msgLog  obj",msgLog);
                logConversation(msgLog);

                var p = Promise.resolve();
                _.each(lines, function(line) {
                    line = line.trim();
                    p = p.then(function() {
                        console.log("=== p line",line);
                        return wait(500).then(function() {
                            return bot.say(line);
                        });
                    });
                });
                return p.then(() => 'speak');
            }

            return updateSilent()
                .then(getSilent)
                .then(processMessage);
        }
    }
});
<commit_msg>trying to break case<commit_after>'use strict';

const _ = require('lodash');
const Script = require('smooch-bot').Script;
var pg = require('pg');
var Q = require("q");
var request = require("request");
var newUser = require("./db");
var logConversation = require("./conversation");
var nlp = require("./nlp");
var pushConv = require("./push");
var newBot_msg = require("./newBot");
//var findSession = require("./sessionsearch");

//var sched = require("./sched");

const scriptRules = require('./script.json');

var msgLog = {
    smoochId: '',
    received: '',
    usermessage: '',
    role: '',
    message_id: '',
    sourcetype: '',
    receivedtime: '',
    responsemessage: '',
    responsetype: '',
    responsetime: '',
    newUsercheck: 'false'
  };

function wait(ms) {
    return new Promise((resolve) => {
        setTimeout(resolve, ms);
    });
}

module.exports = new Script({
    processing: {
        //prompt: (bot) => bot.say('Beep boop...'),
        receive: () => 'processing'
    },

    start: {
        receive: (bot) => {
            return bot.say('Hello!  I\'m A16, the Agile2016 EventBot Concierge.  To find out what I can help you with type MENU for options or KEY for Keywords.')
                .then(() => 'speak');
        }
    },

//    s1: {
//      prompt: (bot) => bot.say('Type part of the session name?'),
//      receive: (bot, message) => {
//        const name = message.text;
//        return bot.setProp('name', name)
//            .then(() => bot.say(`Great! I'll call you ${name}
//Is that OK? %[Yes](postback:yes) %[No](postback:no)`))
//            .then(() => 'speak');
//      }
//    },

//    chris: {
//      receive: (bot, message) => {
//        return bot.getProp('name')
//          .then((name) => bot.say('That is all!'))
//          .then(() => 'speak');
//      }
//    },

    speak: {
        receive: (bot, message) => {

            console.log("===bot message ", message);

            let upperText = message.text.trim().toUpperCase();

//TODO: don't try stateMachine; instead capture last text and if a search thing ie is search by speaker
//then the submit is speaker name and call speakersearch won't work for is session expires
//            if (msgLog.usermessage == 'SBSN') {
//                console.log("=== search string", upperText);
//                var msg = {};
//                findSession(upperText, msg);
//                return bot.setProp("silent", true);
//            }

            msgLog.smoochId = bot.userId;
            msgLog.received = message.received;
            msgLog.usermessage = message.text;
            msgLog.role = message.role;
            msgLog.message_id = message._id;
            msgLog.receivedtime = new Date();
//this mess is my way around the fact that smooch completely  changes the structure of the message obj if it is a postback vs user entered text
//            console.log("===message.message",message.message);
            switch (typeof message.message === "undefined") {
              case false:
//                  console.log("!!!! appMaker = T, message.role", message.message);
                  msgLog.sourcetype = message.action.type;
                break;
              default:
//                  console.log("!!!! appUser = T, message.role", message.source);
                  msgLog.sourcetype = message.source.type;
                break;
            }

            //SK_ACCESS is a heroku config var that has the list of devices smoochids for auth users to send ad hoc push conversations
            var authUsers = process.env.SK_ACCESS

            //Not sure if this is the best way to accomplish not calling newUser everytime, but it seems to work
            if(msgLog.newUsercheck == 'false') {
              console.log("===NewUser");
              newUser(bot)
                .then(msgLog.newUsercheck = 'true');
            }

            //For ad hoc messages - scheduled messages are done differently in checkItems
            if (authUsers.indexOf(bot.userId) !== -1) {
              if (upperText.substr(0,4) == '/SK ') {
                upperText = upperText.substr(0,3);
                newBot_msg(message.text.substr(4));
                console.log("****after push msg:  ",message.text," authUser:  ",authUsers);
              }
            }

//TODO: There is a bug with /support and /a16 now.  /support does not present message and /a16 sometimes doubles the message
            function updateSilent() {
                switch (upperText) {
                    case "CONNECT ME":
                        return bot.setProp("silent", true);
                    case "/SUPPORT":
                        console.log("*** /support", upperText);
//                        processMessage(false);
                        return bot.setProp("silent", true);
                    case "DISCONNECT":
                        return bot.setProp("silent", false);
                    case "/A16":
                        console.log("*** /A16 ",upperText," ***");
                        processMessage(false);
                        return bot.setProp("silent", false);
                    default:
                        return Promise.resolve();
                }
            }

            function getSilent() {
                return bot.getProp("silent");
            }

            function processMessage(isSilent) {
                if (isSilent) {
                    return Promise.resolve("speak");
                }

                var promises = [];
                var source;
                var fulfillmentSpeech;
                var simplified;
                promises.push(nlp(upperText, bot.userId));

                Q.all(promises).then(function(responses) {
                    // response is the JSON from API.ai
                    responses.forEach(function(response) {
                        console.log("===in Q.all");
                        console.log("===received result from API.ai",response);
                        source = response.result.source;
                        if (source && source !== 'agent')
                        {
                            console.log("====Q all not agent");
                            fulfillmentSpeech = response.result.fulfillment.speech;
                            simplified = response.result.parameters.simplified;
                        } else if (source && source == 'agent') {
                          console.log("=== source is agent ", response.result);
                          fulfillmentSpeech = response.result.fulfillment.speech;
                          simplified = response.result.metadata.parameters.event;
                        }
                        console.log("source: ", source);
                        console.log("fulfillmentSpeech: ", fulfillmentSpeech);
                        console.log("simplified: ", simplified);

                        respondMessage(source, fulfillmentSpeech, simplified);
                    });
                }, function(error) {
                    console.log("===Q all error ", error);
                });
            }

            function respondMessage(source, fulfillmentSpeech, simplified)
            {
                console.log("source: ", source);
                console.log("fulfillmentSpeech: ", fulfillmentSpeech);
                console.log("simplified: ", simplified);
                console.log("===receive step 3",upperText);

              //if (source != 'agent')
              console.log("?????? what is the source:  ", source);

              switch (source) {
                case 'blueberries':
                    console.log("===source is ", source);
                    if (fulfillmentSpeech)
                    {
                      switch (simplified) {
                        case "hello":
                          console.log("===in hello");
                          upperText = simplified.trim().toUpperCase();
                          break;
                        case "do you know":
                        case "can you help":
                        case "do you have":
                        case "how does this app work":
                        case "how much time do you need":
                        case "how to open you":
                        case "what can you talk about":
                        case "what do you know":
                          console.log("===in what do you know");
                          upperText = 'KNOW';
                          break;
                        case "what do you do":
                        case "how do you know":
                        case "job":
                          console.log("===in what do do");
                          upperText = "JOB";
                          break;
                        case "do you know me":
                        case "do you remember me":
                          console.log("===do you know me");
                          upperText = "ME";
                          break;
                        case "who named you":
                        console.log("===NAME");
                          upperText = "NAME";
                          break;
                        case "can you hear me":
                        case "can you speak":
                        case "change your":
                        case "hurry":
                        case "talk faster":
                        case "do you drink":
                        case "do you eat":
                          console.log("===set to NULL and question");
                          upperText = "";
                          break;
                        default:
                          console.log("===in switch default");
                          if (upperText == "EVENING") {break;}
                          msgLog.responsemessage = fulfillmentSpeech;
                          msgLog.responsetime = new Date;
                          msgLog.responsetype = 'API.AI Domain';
                          return bot.say(fulfillmentSpeech).then(() => 'speak');
                      }
                    }
                    break;
                  case 'agent':
                    if (simplified == 'agile2017')
                      {
                          console.log("simplified is: ", simplified);
                          msgLog.responsemessage = fulfillmentSpeech;
                          msgLog.responsetime = new Date;
                          msgLog.responsetype = 'API.AI Intent';
                          return bot.say(fulfillmentSpeech).then(() => 'speak');
                      }
                    break;
                  default:
                    console.log("===finished switch, upperText now:",upperText);
                  }

                if (!_.has(scriptRules, upperText)) {
                    console.log("===no rule", upperText);
                    return bot.say(`I'm sorry that is not something I know.  Type MENU or KEY for a list of things I can help you with.`).then(() => 'speak');
                }

                var response = scriptRules[upperText];
                var lines = response.split('\n');
                msgLog.responsemessage = response;
                msgLog.responsetime = new Date;
                msgLog.responsetype = 'JSON';
                console.log("=== msgLog  obj",msgLog);
                logConversation(msgLog);

                var p = Promise.resolve();
                _.each(lines, function(line) {
                    line = line.trim();
                    p = p.then(function() {
                        console.log("=== p line",line);
                        return wait(500).then(function() {
                            return bot.say(line);
                        });
                    });
                });
                return p.then(() => 'speak');
            }

            return updateSilent()
                .then(getSilent)
                .then(processMessage);
        }
    }
});
<|endoftext|>


<commit_before>
let opRegExpStr = '(add|merge|replace|remove)'
let separator = '\\s+'
let pathRegExpStr = '([\\/[a-zA-Z0-9\-]+)'
let pathOptRegExpStr = '(?:\\/[\\/[a-zA-Z0-9]+)'
let objRegExpStr = `(?:{.+?(?=}\s*(;|$)))`
let arrayRegExpStr = `(?:\\[.+?(?=\\]\s*(;|$)))`
let numberRegExpStr = `(?:-?(?:(?:[1-9]\\d*)|0)\\.?\\d*)`
<<<<<<< HEAD
let textRegExpStr = `(?:'.*?')|(?:".*?")`
let booleanRegExpStr = `(?:true|false)`
=======
let textRegExpStr = `(?:'.*?')|(?:".*?")`
>>>>>>> 872dbe185ae4391dfabe768928cfce008176db46
let htmlAttrRegExpStr = `(?:attr\.[^\\t\\n\\f\\s\\/>"'=]+)`

// @TODO: Split the remove patch test in another regex
// so that we can find precise matches for update operations
let patchReg =
  opRegExpStr
  + separator
  + pathRegExpStr
  + '(?:'
    + separator
    + '(' + pathOptRegExpStr + '|' + arrayRegExpStr + '|' + htmlAttrRegExpStr + '|' + textRegExpStr + '|' + numberRegExpStr + '|' + objRegExpStr + '|' + booleanRegExpStr + ')'
  + ')?'

function parsePatch(x) {

  let reg = new RegExp(patchReg, 'gi')

  let found
  let results = []

  while ((found = reg.exec(x)) !== null) {
    let op = found[1]
    let path = found[2]
    let value = found[3]

    let patch = {
      op,
      path
    }

    if (op !== 'remove') {
      patch.value = value

// @TODO: Find a way to include the trailing '}' char in the object
// regexp selection, until then this is a hack
      if (patch.value[0] === '{') {
        patch.value += '}'
      } else if (patch.value[0] === '[') {
        patch.value += ']'
      }
    }

    results.push(patch)
  }

  return results
}

export { objRegExpStr }
export { booleanRegExpStr }
export { htmlAttrRegExpStr }
export { arrayRegExpStr }
export { numberRegExpStr }
export { textRegExpStr }
export { pathOptRegExpStr }
export default parsePatch
<commit_msg>Add boolean support for jsonmvc-module.ui.<commit_after>
let opRegExpStr = '(add|merge|replace|remove)'
let separator = '\\s+'
let pathRegExpStr = '([\\/[a-zA-Z0-9\-]+)'
let pathOptRegExpStr = '(?:\\/[\\/[a-zA-Z0-9]+)'
let objRegExpStr = `(?:{.+?(?=}\s*(;|$)))`
let arrayRegExpStr = `(?:\\[.+?(?=\\]\s*(;|$)))`
let numberRegExpStr = `(?:-?(?:(?:[1-9]\\d*)|0)\\.?\\d*)`
let textRegExpStr = `(?:'.*?')|(?:".*?")`
let booleanRegExpStr = `(?:true|false)`
let htmlAttrRegExpStr = `(?:attr\.[^\\t\\n\\f\\s\\/>"'=]+)`

// @TODO: Split the remove patch test in another regex
// so that we can find precise matches for update operations
let patchReg =
  opRegExpStr
  + separator
  + pathRegExpStr
  + '(?:'
    + separator
    + '(' + pathOptRegExpStr + '|' + arrayRegExpStr + '|' + htmlAttrRegExpStr + '|' + textRegExpStr + '|' + numberRegExpStr + '|' + objRegExpStr + '|' + booleanRegExpStr + ')'
  + ')?'

function parsePatch(x) {

  let reg = new RegExp(patchReg, 'gi')

  let found
  let results = []

  while ((found = reg.exec(x)) !== null) {
    let op = found[1]
    let path = found[2]
    let value = found[3]

    let patch = {
      op,
      path
    }

    if (op !== 'remove') {
      patch.value = value

// @TODO: Find a way to include the trailing '}' char in the object
// regexp selection, until then this is a hack
      if (patch.value[0] === '{') {
        patch.value += '}'
      } else if (patch.value[0] === '[') {
        patch.value += ']'
      }
    }

    results.push(patch)
  }

  return results
}

export { objRegExpStr }
export { booleanRegExpStr }
export { htmlAttrRegExpStr }
export { arrayRegExpStr }
export { numberRegExpStr }
export { textRegExpStr }
export { pathOptRegExpStr }
export default parsePatch
<|endoftext|>


<commit_before>COLOR_CHANGE_INTERVAL = 100
var h1 = document.getElementsByTagName("h1");
var h2 = document.getElementsByTagName("h2");
function colorize(headers, color) {
  for (var i=0; i<headers.length; i++) {
    headers[i].style.color = color
  }
}
var colors = ["red", "orange", "yellow", "green", "blue", "indigo", "violet"];
var i = 0;
function getRandomColor() {
  // this avoids color repeats
  i = (i + 1 + Math.floor(Math.random() * (colors.length - 1))) % colors.length;
  return colors[i];
}
function randomColorize(headers) {
  for (var i=0; i<headers.length; i++) {
    headers[i].style.color = getRandomColor();
  }
}
function randomlyColorizeHeaders() {
  randomColorize(h1);
  randomColorize(h2);
}
window.setInterval(randomlyColorizeHeaders, COLOR_CHANGE_INTERVAL);
<commit_msg>make sure color always changes<commit_after>COLOR_CHANGE_INTERVAL = 100
var h1 = document.getElementsByTagName("h1");
var h2 = document.getElementsByTagName("h2");
var colors = ["red", "orange", "yellow", "green", "blue", "indigo", "violet"];
function getDifferentRandomColor(text) {
  var i = colors.indexOf(text.style.color);
  i = (i + 1 + Math.floor(Math.random() * (colors.length - 1))) % colors.length;
  return colors[i];
}
function randomColorize(headings) {
  for (var i=0; i<headings.length; i++) {
    headings[i].style.color = getDifferentRandomColor(headings[i]);
  }
}
function randomlyColorizeHeadings() {
  randomColorize(h1);
  randomColorize(h2);
}
window.setInterval(randomlyColorizeHeadings, COLOR_CHANGE_INTERVAL);
<|endoftext|>


<commit_before>import expect from 'expect.js'
import {stripIndent} from 'common-tags'
import {create} from 'jss'
import functionPlugin from 'jss-plugin-rule-value-function'

import camelCase from './index'

const settings = {
  createGenerateId: () => rule => `${rule.key}-id`
}

describe('jss-plugin-camel-case', () => {
  let jss

  beforeEach(() => {
    jss = create(settings).use(camelCase())
  })

  describe('regular rule', () => {
    let sheet

    beforeEach(() => {
      sheet = jss.createStyleSheet({
        a: {
          fontSize: '20px',
          zIndex: 1,
          lineHeight: 1.2
        }
      })
    })

    it('should generate correct CSS', () => {
      expect(sheet.toString()).to.be(
        '.a-id {\n  font-size: 20px;\n  z-index: 1;\n  line-height: 1.2;\n}'
      )
    })
  })

  describe('@font-face with array of styles', () => {
    let sheet

    beforeEach(() => {
      sheet = jss.createStyleSheet({
        '@font-face': [
          {
            fontFamily: 'Lato-Light',
            src: 'url("/fonts/Lato-Light.ttf") format("truetype")'
          },
          {
            fontFamily: 'Lato-Bold',
            src: 'url("/fonts/Lato-Bold.ttf") format("truetype")'
          }
        ]
      })
    })

    it('should generate correct CSS', () => {
      expect(sheet.toString()).to.be(
        '@font-face {\n' +
          '  font-family: Lato-Light;\n' +
          '  src: url("/fonts/Lato-Light.ttf") format("truetype");\n' +
          '}\n' +
          '@font-face {\n' +
          '  font-family: Lato-Bold;\n' +
          '  src: url("/fonts/Lato-Bold.ttf") format("truetype");\n' +
          '}'
      )
    })
  })

  describe('fallbacks object', () => {
    let sheet

    beforeEach(() => {
      sheet = jss.createStyleSheet({
        '@font-face': {
          fontFamily: 'MyWebFont',
          fallbacks: {
            fontFamily: 'MyWebFontFallback'
          }
        }
      })
    })

    it('should generate correct CSS', () => {
      expect(sheet.toString()).to.be(
        '@font-face {\n' +
          '  font-family: MyWebFontFallback;\n' +
          '  font-family: MyWebFont;\n' +
          '}'
      )
    })
  })

  describe('fallbacks array', () => {
    let sheet

    beforeEach(() => {
      sheet = jss.createStyleSheet({
        '@font-face': {
          fontFamily: 'MyWebFont',
          fallbacks: [{fontFamily: 'MyWebFontFallback'}]
        }
      })
    })

    it('should generate correct CSS', () => {
      expect(sheet.toString()).to.be(
        '@font-face {\n' +
          '  font-family: MyWebFontFallback;\n' +
          '  font-family: MyWebFont;\n' +
          '}'
      )
    })
  })

  describe('font faces with fallbacks', () => {
    let sheet

    beforeEach(() => {
      sheet = jss.createStyleSheet({
        '@font-face': [
          {
            fontFamily: 'MyWebFont',
            fallbacks: {
              fontFamily: 'MyWebFontFallback'
            }
          }
        ]
      })
    })

    it('should generate correct CSS', () => {
      expect(sheet.toString()).to.be(
        '@font-face {\n' +
          '  font-family: MyWebFontFallback;\n' +
          '  font-family: MyWebFont;\n' +
          '}'
      )
    })
  })

  describe('function values', () => {
    let sheet

    beforeEach(() => {
      const localJss = create(settings).use(functionPlugin(), camelCase())
      sheet = localJss.createStyleSheet({
        a: {
          fontSize: () => 12
        }
      })
    })

    it('should generate correct CSS', () => {
      sheet.update()
      expect(sheet.toString()).to.be(stripIndent`
        .a-id {
          font-size: 12;
        }
      `)
    })
  })

  describe('css variables', () => {
    let localJss
    beforeEach(() => {
      localJss = create(settings).use(functionPlugin(), camelCase())
    })

    it('with static css variable', () => {
      const sheet = localJss.createStyleSheet({
        a: {
          '--fontSize': 12,
        }
      })
      expect(sheet.toString()).to.be(stripIndent`
        .a-id {
          --fontSize: 12;
        }
      `)
    })

    it('with dynamic css variable', () => {
      const sheet = localJss.createStyleSheet({
        a: {
          '--fontSize': size => size,
        }
      })

      sheet.update(12);

      expect(sheet.toString()).to.be(stripIndent`
        .a-id {
          --fontSize: 12;
        }
      `)

      sheet.update(16);

      expect(sheet.toString()).to.be(stripIndent`
        .a-id {
          --fontSize: 16;
        }
      `)
    })
  })
})
<commit_msg>Revert "Add tests for css variables in camel case plugins"<commit_after>import expect from 'expect.js'
import {stripIndent} from 'common-tags'
import {create} from 'jss'
import functionPlugin from 'jss-plugin-rule-value-function'

import camelCase from './index'

const settings = {
  createGenerateId: () => rule => `${rule.key}-id`
}

describe('jss-plugin-camel-case', () => {
  let jss

  beforeEach(() => {
    jss = create(settings).use(camelCase())
  })

  describe('regular rule', () => {
    let sheet

    beforeEach(() => {
      sheet = jss.createStyleSheet({
        a: {
          fontSize: '20px',
          zIndex: 1,
          lineHeight: 1.2
        }
      })
    })

    it('should generate correct CSS', () => {
      expect(sheet.toString()).to.be(
        '.a-id {\n  font-size: 20px;\n  z-index: 1;\n  line-height: 1.2;\n}'
      )
    })
  })

  describe('@font-face with array of styles', () => {
    let sheet

    beforeEach(() => {
      sheet = jss.createStyleSheet({
        '@font-face': [
          {
            fontFamily: 'Lato-Light',
            src: 'url("/fonts/Lato-Light.ttf") format("truetype")'
          },
          {
            fontFamily: 'Lato-Bold',
            src: 'url("/fonts/Lato-Bold.ttf") format("truetype")'
          }
        ]
      })
    })

    it('should generate correct CSS', () => {
      expect(sheet.toString()).to.be(
        '@font-face {\n' +
          '  font-family: Lato-Light;\n' +
          '  src: url("/fonts/Lato-Light.ttf") format("truetype");\n' +
          '}\n' +
          '@font-face {\n' +
          '  font-family: Lato-Bold;\n' +
          '  src: url("/fonts/Lato-Bold.ttf") format("truetype");\n' +
          '}'
      )
    })
  })

  describe('fallbacks object', () => {
    let sheet

    beforeEach(() => {
      sheet = jss.createStyleSheet({
        '@font-face': {
          fontFamily: 'MyWebFont',
          fallbacks: {
            fontFamily: 'MyWebFontFallback'
          }
        }
      })
    })

    it('should generate correct CSS', () => {
      expect(sheet.toString()).to.be(
        '@font-face {\n' +
          '  font-family: MyWebFontFallback;\n' +
          '  font-family: MyWebFont;\n' +
          '}'
      )
    })
  })

  describe('fallbacks array', () => {
    let sheet

    beforeEach(() => {
      sheet = jss.createStyleSheet({
        '@font-face': {
          fontFamily: 'MyWebFont',
          fallbacks: [{fontFamily: 'MyWebFontFallback'}]
        }
      })
    })

    it('should generate correct CSS', () => {
      expect(sheet.toString()).to.be(
        '@font-face {\n' +
          '  font-family: MyWebFontFallback;\n' +
          '  font-family: MyWebFont;\n' +
          '}'
      )
    })
  })

  describe('font faces with fallbacks', () => {
    let sheet

    beforeEach(() => {
      sheet = jss.createStyleSheet({
        '@font-face': [
          {
            fontFamily: 'MyWebFont',
            fallbacks: {
              fontFamily: 'MyWebFontFallback'
            }
          }
        ]
      })
    })

    it('should generate correct CSS', () => {
      expect(sheet.toString()).to.be(
        '@font-face {\n' +
          '  font-family: MyWebFontFallback;\n' +
          '  font-family: MyWebFont;\n' +
          '}'
      )
    })
  })

  describe('function values', () => {
    let sheet

    beforeEach(() => {
      const localJss = create(settings).use(functionPlugin(), camelCase())
      sheet = localJss.createStyleSheet({
        a: {
          fontSize: () => 12
        }
      })
    })

    it('should generate correct CSS', () => {
      sheet.update()
      expect(sheet.toString()).to.be(stripIndent`
        .a-id {
          font-size: 12;
        }
      `)
    })
  })
})
<|endoftext|>


<commit_before>var c = document.getElementById("c"),
    ctx,
    w = window.innerWidth,
    h = window.innerHeight,
    ball = {x:w/2, y:h/2, vx:0, vy:h/230},
    p1 = {x:w/2, dir:0, w:w/7},
    p2 = {x:w/2, dir:0, w:w/7};
var tmp = (w<h)?w:h;
ball.r = tmp/80;
c.width = w;
c.height = h;
ctx = c.getContext("2d");
c.ontouchstart = c.ontouchmove = function(e){
	e.preventDefault();
	p1.dir = 0;
	p2.dir = 0;
	for(var i = 0;i<e.touches.length;i++){
		var cur = e.touches[i];
		var p = p1;
		if(cur.pageY<=h/2){
			p = p2;
		}
		if(cur.pageX<w/2){
			p.dir = -1;
		}else{
			p.dir = 1;
		}
	}
}
c.ontouchend = function(e){
	if(e.touches.length>0){
		c.ontouchstart(e);
	}else{
		p1.dir = 0;
		p2.dir = 0;
	}
}
setInterval(function(){
	ctx.clearRect(0, 0, w, h);
	ctx.beginPath();
	ctx.fillStyle = "white";
	ctx.fillRect(p1.x - p1.w/2, h - 27, p1.w, 12); //draw p1 paddle
	ctx.fillRect(p2.xp2.w/2, 15, p1.w, 12); //draw p2 paddle
	ctx.arc(ball.x, ball.y, ball.r, 0, 8); //draw ball
	ctx.fill();
	p1.x += p1.dir*w/100;
	p2.x += p2.dir*w/100;
	if(p1.x<p1.w/2+10){
		p1.x = p1.w/2+10;
	}else if(p1.x>w-p1.w/2-10){
		p1.x = w-p1.w/2-10;
	}
	if(p2.x<p2.w/2+10){
		p2.x = p2.w/2+10;
	}else if(p2.x>w-p2.w/2-10){
		p2.x = w-p2.w/2-10;
	}
	ball.x += ball.vx;
	ball.y += ball.vy;
	if(ball.x<ball.r){
		ball.x = ball.r;
		ball.vx = -ball.vx;
	}else if(ball.x>w-ball.r){
		ball.x = w-ball.r;
		ball.vx = -ball.vx;
	}
	if(ball.y+ball.r>h-27&&ball.y+ball.r<h-27+h/150+1&&ball.x>=p1.x-p1.w/2&&ball.x<=p1.x+p1.w/2){
		ball.y = h-27-ball.r;
		var a = Math.random()*-(Math.PI/2)-Math.PI/4;
		ball.vy = Math.sin(a)*h/150;
		ball.vx = Math.cos(a)*h/150;
	}else if(ball.y-ball.r<27&&ball.y-ball.r>27-h/150-1&&ball.x>=p2.x-p2.w/2&&ball.x<=p2.x+p2.w/2){
		ball.y = 27+ball.r;
		ball.vy = -ball.vy;
	}
}, 10)
<commit_msg>fixed typos<commit_after>var c = document.getElementById("c"),
    ctx,
    w = window.innerWidth,
    h = window.innerHeight,
    ball = {x:w/2, y:h/2, vx:0, vy:h/230},
    p1 = {x:w/2, dir:0, w:w/7},
    p2 = {x:w/2, dir:0, w:w/7};
var tmp = (w<h)?w:h;
ball.r = tmp/80;
c.width = w;
c.height = h;
ctx = c.getContext("2d");
c.ontouchstart = c.ontouchmove = function(e){
	e.preventDefault();
	p1.dir = 0;
	p2.dir = 0;
	for(var i = 0;i<e.touches.length;i++){
		var cur = e.touches[i];
		var p = p1;
		if(cur.pageY<=h/2){
			p = p2;
		}
		if(cur.pageX<w/2){
			p.dir = -1;
		}else{
			p.dir = 1;
		}
	}
}
c.ontouchend = function(e){
	if(e.touches.length>0){
		c.ontouchstart(e);
	}else{
		p1.dir = 0;
		p2.dir = 0;
	}
}
setInterval(function(){
	ctx.clearRect(0, 0, w, h);
	ctx.beginPath();
	ctx.fillStyle = "white";
	ctx.fillRect(p1.x - p1.w/2, h - 27, p1.w, 12); //draw p1 paddle
	ctx.fillRect(p2.x - p2.w/2, 15, p1.w, 12); //draw p2 paddle
	ctx.arc(ball.x, ball.y, ball.r, 0, 8); //draw ball
	ctx.fill();
	p1.x += p1.dir*w/100;
	p2.x += p2.dir*w/100;
	if(p1.x<p1.w/2+10){
		p1.x = p1.w/2+10;
	}else if(p1.x>w-p1.w/2-10){
		p1.x = w-p1.w/2-10;
	}
	if(p2.x<p2.w/2+10){
		p2.x = p2.w/2+10;
	}else if(p2.x>w-p2.w/2-10){
		p2.x = w-p2.w/2-10;
	}
	ball.x += ball.vx;
	ball.y += ball.vy;
	if(ball.x<ball.r){
		ball.x = ball.r;
		ball.vx = -ball.vx;
	}else if(ball.x>w-ball.r){
		ball.x = w-ball.r;
		ball.vx = -ball.vx;
	}
	if(ball.y+ball.r>h-27&&ball.y+ball.r<h-27+h/150+1&&ball.x>=p1.x-p1.w/2&&ball.x<=p1.x+p1.w/2){
		ball.y = h-27-ball.r;
		var a = Math.random()*-(Math.PI/2)-Math.PI/4;
		ball.vy = Math.sin(a)*h/150;
		ball.vx = Math.cos(a)*h/150;
	}else if(ball.y-ball.r<27&&ball.y-ball.r>27-h/150-1&&ball.x>=p2.x-p2.w/2&&ball.x<=p2.x+p2.w/2){
		ball.y = 27+ball.r;
		ball.vy = -ball.vy;
	}
}, 10)
<|endoftext|>


<commit_before>//  The calculator pseudoClass represents a four-function calculator.
//  The following both defines and constructs the singleton calculator object.

var calculator = new function(){

//  model pseudoclass is an internal representation of
//  the calculator's state.
//
//  By defining it as a var inside the pseudoclass, it
//  and its properties, and its methods are invisible
//  to the outside world.

    var model = {

        tally : 0, // running total since last clear

        operator : '+', // last operator pressed

        temp : 0, // number, as it entered by user

        isClear : true, // flag indicates whether there has been output

//      The evaluate function performs a computation.
//      First, it performs the stored operation on temp and tally.
//      Then it stores the given operation for the next evaluation.
//
//      Returns the state to show to user.

        evaluate : function(operate){

//          Special condition if there has not been output yet.

            if( this.isClear ){

                this.tally = this.temp;
                this.isClear = false;

            } else {

//              Perform the stored operation on tally and temp

                switch (this.operator){

                    case '+':
                        this.tally += this.temp;
                        break;

                    case '-':
                        this.tally -= this.temp;
                        break;

                    case 'x':
                        this.tally *= this.temp;
                        break;

                    case '/':
                        this.tally /= this.temp;
                        break;

                }
            }

//          In either case, update the display, and prepare for next input

            this.temp = 0; // reset temp
            this.operator = operate; // get ready to perform the given operation

            return this.tally; // user sees result of computation
        },

//      The update function will take string representing a single digit, and
//      append it to what the user has entered so far, both on the screen and
//      internally.
//      Returns the state to show to user.

        update : function(lastDigit){
            this.temp = (10 * this.temp) + parseInt(lastDigit);
            return this.temp; // user sees number entered thus far
        },

//      The reset function clears the calculator's instance variables, and
//      wipes the screen.

        reset : function(){
            this.tally = 0;
            this.operator = '+';
            this.temp = 0;
            this.isClear = true;
            return 0;
        }

    }; // end model

//  view is the representation of the screen.

    var view = {

//      the object holding the output.

        display : {innerHTML : "0"},

//      method to set display to given number

        output : function( val ){

            if( typeof val === 'number' ){

                this.display.innerHTML = val;
            }
        },

//      Sets sets given object to be the new display.
//      Copies the old output into new display.

        setDisplay : function( newScreen ){ // function

            if( typeof newScreen === 'object' ){

                newScreen.innerHTML = this.display.innerHTML;

                this.display = newScreen;
            }
        }
    }; // end view

//  These are the calculator's publicly facing methods.
//  They should be consided the controller.

//  Enter number is called when a user presses a digit, and takes that
//  digit as a param. If possible, enterNumber will pass that digit
//  to the inner calculator's update method.

    this.enterNumber = function(num){

        if( 'update' in model ){

//          Pass response from model's update method to the view
            view.output( model.update(num) );
        }
    };

//  Enter operator is called when a user presses an operator, and takes that
//  operator as a param. If possible, enterOperator will pass that operator
//  to the inner calculator's evaluate method.

    this.enterOperator = function(opp){

        if( 'evaluate' in model ){

//          Pass response from model's evaluate method to the view
            view.output( model.evaluate(opp) );
        }
    };

//  Clear is called when a user presses the clear button, and takes
//  no params. If possible, clear will call the inner calculator's
//  reset method.

    this.clear = function(){

        if( 'reset' in model ){

//          Pass response from model's reset method to the view
            view.output( model.reset() );
        }
    };

//  Set Display takes an object, and set it to be the
//  Calculator's new output field.

    this.setDisplay = function( obj ){

      view.setDisplay(obj);
    };

}; // end calculator definition

//    This is where the setup for the calculator happens. It's all
//    inside an anonymous function, so  the vars and the functions
//    will go away at the end.
//    (But if it defines getElementsByClassName, that will not.)

(function(){

//  First, I will check if document.getElementsByClassName is natifley
//  defined, and define it myself if not.

    checkGetClass();

//  Second, run setup routine, in which page elements are bound to
//  calculator methods.

//  Start by calling the calcuator's init method, passing
//  in an output element to be the display.

    calculator.setDisplay(document.getElementById('out'));

//  Move onto binding the numbers' click
//  event to the calculator's update method.

    var numbers = document.getElementsByClassName('number');

    for (var i = 0; i < numbers.length; i++) {

//      clicking a number will update the display and the temp variable

        numbers[i].onclick = function(){
              calculator.enterNumber(this.value); // this = a number btn
        }
    }

    var operators = document.getElementsByClassName('operator');

    for( var k = 0; k < operators.length; k++){

//      clicking an operator will make the calculator perform an operation.

        operators[k].onclick = function(){
              calculator.enterOperator(this.value); // this = a function btn
        }

    }

//  lastly, bind the clear function to the clear buttons' click event

    document.getElementById('clear').onclick = function(){
          calculator.clear();
    }

    function checkGetClass(){

        if (!('getElementsByClassName' in document)){

//         The following implementation is thanks to:
//         http://ejohn.org/blog/getelementsbyclassname-speed-comparison/#js-4

            document.getElementsByClassName = function(className, parentElement) {
                if (Prototype.BrowserFeatures.XPath) {
                    var q = ".//*[contains(concat(' ', @class, ' '), ' " + className + " ')]";
                    return document._getElementsByXPath(q, parentElement);
                } else {
                    var children = ($(parentElement) || document.body).getElementsByTagName('*');
                    var elements = [], child;
                    for (var i = 0, length = children.length; i < length; i++) {
                    child = children[i];
                    if (Element.hasClassName(child, className))
                        elements.push(Element.extend(child));
                    }
                    return elements;
                }
            };
            // end implementation

      } // end if

    } // end checkGetClass

})(); // end the anonymouse function, and invoke it
<commit_msg>The alt. implementation of getClass used jQuery, which I am not including.<commit_after>//  The calculator pseudoClass represents a four-function calculator.
//  The following both defines and constructs the singleton calculator object.

var calculator = new function(){

//  model pseudoclass is an internal representation of
//  the calculator's state.
//
//  By defining it as a var inside the pseudoclass, it
//  and its properties, and its methods are invisible
//  to the outside world.

    var model = {

        tally : 0, // running total since last clear

        operator : '+', // last operator pressed

        temp : 0, // number, as it entered by user

        isClear : true, // flag indicates whether there has been output

//      The evaluate function performs a computation.
//      First, it performs the stored operation on temp and tally.
//      Then it stores the given operation for the next evaluation.
//
//      Returns the state to show to user.

        evaluate : function(operate){

//          Special condition if there has not been output yet.

            if( this.isClear ){

                this.tally = this.temp;
                this.isClear = false;

            } else {

//              Perform the stored operation on tally and temp

                switch (this.operator){

                    case '+':
                        this.tally += this.temp;
                        break;

                    case '-':
                        this.tally -= this.temp;
                        break;

                    case 'x':
                        this.tally *= this.temp;
                        break;

                    case '/':
                        this.tally /= this.temp;
                        break;

                }
            }

//          In either case, update the display, and prepare for next input

            this.temp = 0; // reset temp
            this.operator = operate; // get ready to perform the given operation

            return this.tally; // user sees result of computation
        },

//      The update function will take string representing a single digit, and
//      append it to what the user has entered so far, both on the screen and
//      internally.
//      Returns the state to show to user.

        update : function(lastDigit){
            this.temp = (10 * this.temp) + parseInt(lastDigit);
            return this.temp; // user sees number entered thus far
        },

//      The reset function clears the calculator's instance variables, and
//      wipes the screen.

        reset : function(){
            this.tally = 0;
            this.operator = '+';
            this.temp = 0;
            this.isClear = true;
            return 0;
        }

    }; // end model

//  view is the representation of the screen.

    var view = {

//      the object holding the output.

        display : {innerHTML : "0"},

//      method to set display to given number

        output : function( val ){

            if( typeof val === 'number' ){

                this.display.innerHTML = val;
            }
        },

//      Sets sets given object to be the new display.
//      Copies the old output into new display.

        setDisplay : function( newScreen ){ // function

            if( typeof newScreen === 'object' ){

                newScreen.innerHTML = this.display.innerHTML;

                this.display = newScreen;
            }
        }
    }; // end view

//  These are the calculator's publicly facing methods.
//  They should be consided the controller.

//  Enter number is called when a user presses a digit, and takes that
//  digit as a param. If possible, enterNumber will pass that digit
//  to the inner calculator's update method.

    this.enterNumber = function(num){

        if( 'update' in model ){

//          Pass response from model's update method to the view
            view.output( model.update(num) );
        }
    };

//  Enter operator is called when a user presses an operator, and takes that
//  operator as a param. If possible, enterOperator will pass that operator
//  to the inner calculator's evaluate method.

    this.enterOperator = function(opp){

        if( 'evaluate' in model ){

//          Pass response from model's evaluate method to the view
            view.output( model.evaluate(opp) );
        }
    };

//  Clear is called when a user presses the clear button, and takes
//  no params. If possible, clear will call the inner calculator's
//  reset method.

    this.clear = function(){

        if( 'reset' in model ){

//          Pass response from model's reset method to the view
            view.output( model.reset() );
        }
    };

//  Set Display takes an object, and set it to be the
//  Calculator's new output field.

    this.setDisplay = function( obj ){

      view.setDisplay(obj);
    };

}; // end calculator definition

//    This is where the setup for the calculator happens. It's all
//    inside an anonymous function, so  the vars and the functions
//    will go away at the end.
//    (But if it defines getElementsByClassName, that will not.)

(function(){

//  First, I will check if document.getElementsByClassName is natifley
//  defined, and define it myself if not.

    checkGetClass();

//  Second, run setup routine, in which page elements are bound to
//  calculator methods.

//  Start by calling the calcuator's init method, passing
//  in an output element to be the display.

    calculator.setDisplay(document.getElementById('out'));

//  Move onto binding the numbers' click
//  event to the calculator's update method.

    var numbers = document.getElementsByClassName('number');

    for (var i = 0; i < numbers.length; i++) {

//      clicking a number will update the display and the temp variable

        numbers[i].onclick = function(){
              calculator.enterNumber(this.value); // this = a number btn
        }
    }

    var operators = document.getElementsByClassName('operator');

    for( var k = 0; k < operators.length; k++){

//      clicking an operator will make the calculator perform an operation.

        operators[k].onclick = function(){
              calculator.enterOperator(this.value); // this = a function btn
        }

    }

//  lastly, bind the clear function to the clear buttons' click event

    document.getElementById('clear').onclick = function(){
          calculator.clear();
    }

    function checkGetClass(){

        if (!('getElementsByClassName' in document)){

//         The following implementation is thanks to:
//         http://ejohn.org/blog/getelementsbyclassname-speed-comparison/#js-3

            document.getElementsByClassName = function (searchClass,node,tag) {
                var classElements = new Array();
                if ( node == null )
                    node = document;
                if ( tag == null )
                    tag = '*';
                var els = node.getElementsByTagName(tag);
                var elsLen = els.length;
                var pattern = new RegExp("(^|\\s)"+searchClass+"(\\s|$)");
                for (i = 0, j = 0; i < elsLen; i++) {
                    if ( pattern.test(els[i].className) ) {
                    classElements[j] = els[i];
                    j++;
                    }
                }
                return classElements;
            };

            // end implementation

      } // end if

    } // end checkGetClass

})(); // end the anonymouse function, and invoke it
<|endoftext|>


<commit_before>// BEGIN SCRIPT.JS FILE
//
    // stores content (html, including li tags) of the "To do" list
    var $list = $('.list').html();
    // stores content (html, including li tags) of the "Done" list
    var $listdone = $('.listdone').html();

    var itemTooltip = "Click to mark the task as completed";
    var itemdoneTooltip = "Click to move the task back to the To Do list, Shift-Click to DELETE the task";
    var alldoneTooltip = "Click to mark all tasks as completed";
    var undoTooltip = "Click to move all tasks back to the To do list";
    var deleteTooltip = "Click to DELETE all completed tasks";

$(document).ready( function() {

    var $list = localStorage.getItem('todolist');
    var $listdone = localStorage.getItem('donelist');
    console.log("after localStorage call, $list is:", $list);
    console.log("after localStorage recall, $listdone is", $listdone);

    updateTooltipsContent();

    // displays the content of "To do" and "Done" lists as recalled from LocalStorage
    $('.list').val($list);
    $('.listdone').val($listdone);
    if ( $list == undefined) {
        // do nothing
        }
    else {
        document.querySelector('.list').innerHTML = $list;
    }

    if ( $listdone == undefined) {
        // do nothing
    } else {
        document.querySelector('.listdone').innerHTML = $listdone;
    }

  // gives focus to input text box as soon document is ready
  $('#newTaskTextInput').focus();

  // Uncomment the following block to print helpful message(s) to the console (useful for DEBUG)
/*
    console.log("DEBUT TEST DEBUG");
    console.log("When page is first loaded, text input= " + $('input[name=newTaskTextInput]').val());
*/

  /* will fix this bug:
     "when user hits enter key, the page is reloaded"
     with the help of http://stackoverflow.com/a/15488912 */
  $('form').on('submit', function(event) {
      event.preventDefault();
  });

  //watches for the click event on div '#add'
  $('#add').click( function() {
      addNewTask();
  });

  if ( $('#show-tooltips-checkbox').is(':checked') ) {
    console.log("#show-tooltips-checkbox is checked");
  } else {
    console.log("#show-tooltips-checkbox is NOT checked");
  }

  function updateTooltipsContent () {
    if ( $('#show-tooltips-checkbox').is(':checked') ) {
      itemTooltip = "Click to mark the task as completed";
      itemdoneTooltip = "Click to move the task back to the To Do list, Shift-Click to DELETE the task";
      alldoneTooltip = "Click to mark all tasks as completed";
      undoTooltip = "Click to move all tasks back to the To do list";
      deleteTooltip = "Click to DELETE all completed tasks";
    } else {
      itemTooltip = "";
      itemdoneTooltip = "";
      alldoneTooltip = "";
      undoTooltip = "";
      deleteTooltip = "";
    }

    $('.item').prop('title', itemTooltip);
    $('.itemdone').prop('title', itemdoneTooltip);
    $('#alldone').prop('title', alldoneTooltip);
    $('#undo').prop('title', undoTooltip);
    $('#delete').prop('title', deleteTooltip);

  };

  /* === FUNCTION TO HANDLE WHAT HAPPENS WHEN YOU CLICK THE ADD "BUTTON"
   Basically, when '#add' button is clicked, the function gets the text
   typed in the input text box whose name is '#newTaskTextInput'
   and appends a new "listItem" list entry (li) within the "todoList" div */

    function addNewTask () {

    // Uncomment the following block to print helpful messages to the console (useful for DEBUG)
/*
       console.log();
       console.log("NOW ENTERING THE addNewTask FUNCTION");
       console.log("input[name=newTaskTextInput] contains the following string '" + $('input[name=newTaskTextInput]').val() + "'");
*/

        //stores text from text input into the itemText variable
        var itemText = $('input[name=newTaskTextInput]').val();

        // Uncomment the following block to print helpful messages to the console (useful for DEBUG)
/*
           console.log("Text entered in the input box is passed to variable itemText.");
           console.log("Now, itemText variable contains '" + itemText + "'");
*/

        /* condition to detect if the user has not modified the default textinput value
           (i.e. still displaying 'enter new task here')
           or if user has clicked (given focus to) text input but not typed text yet;
           ideal case would be to check for any number of white spaces with a regex */
        if(itemText == '' || itemText == ' ' || itemText == '  ' || itemText == 'enter new task here') {
        /* prevents from adding a new task if "#add" div is clicked
           whereas nothing has been typed in the input text box;
           in this case, there is no subsequent action except displaying an error message
           that is appended to the ".error-message" class (to control formatting with CSS) */

          // Uncomment the following line(s) to print helpful message(s) to the console (useful for DEBUG)
//           console.log("No significant text in input box, so displaying error message");

          // actually displays the error message
          $('.error-message').append('Please enter a new task before clicking the add button!
');
        }

        else { // user has actually typed text in the newTaskTextInput field;

            // Uncomment the following block to print helpful message to the console (useful for DEBUG)
/*
            console.log();
            console.log("User has entered text in input box");
            console.log("input[name=newTaskTextInput] contains the following string '" + $('input[name=newTaskTextInput]').val() + "'");
            console.log("Text entered is stored in itemText variable");
            console.log("itemText now contains: ' " + itemText +  "'");
*/

          //appends a new list item, containing the text typed by the user, to the '.list' div
          $('.list').append('
 ' + itemText + '
');

          // Uncomment the following line to print helpful message(s) to the console (useful for DEBUG)
//        console.log("This text from the itemText variable has been added to .list div");

          // empties the '.error-message' div because a new task was actually created
          $('.error-message').empty();

          // Uncomment the following line to print helpful message(s) to the console (useful for DEBUG)
 //          console.log("The .error-message class content has been emptied");

          // clears the input text box;
          $('input[name=newTaskTextInput]').val('');

          // Uncomment the following block to print helpful message to the console (useful for DEBUG)
/*
             console.log("We're at end of the addNewTask function, and itemText variable = " + itemText);
             console.log("And input[name=newTaskTextInput] contains '" + $('input[name=newTaskTextInput]').val() + "'");
*/

        };

        // reflect changes to $list and $listdone and saves them to LocalStorage
        updateTodoList();
        updateDoneList();
        saveToLocalStorage();

        return false; // makes sure pressing Enter will not reload the page
    };

  // === END OF FUNCTION HANDLING NEW TASKS ===

  /* The little function below toggles 'itemdone' class
     on items (from the '.list') that are clicked
     so that the item is greyed out and formatted as strikethrough;
     it also moves the item to the '.listdone' div */
  $(document).on('click', '.item', function() {
      $(this).toggleClass('itemdone');
      $(this).prop('title', itemdoneTooltip);
      // moves the done item to the 'listdone' div
      $('.listdone').append(this);
      // reflects changes to $list and $listdone
      updateTodoList();
      updateDoneList();
      saveToLocalStorage();
  });

  /* The little function below does the opposite
     e.g. moving back a "done" item to the "todo" list when clicked.
     IF SHIFT CLICK, item is DELETED. */
  $(document).on('click', '.itemdone', function(e) {
      if (e.shiftKey) { // if Shift-Click instead of a simple Click
        $(this).remove(); // individual item is deleted
      } else {
        $(this).removeClass('itemdone');
        $(this).prop('title', itemTooltip);
        // moves back the undone item to the 'list' div
        $('.list').append(this);
      }
      // reflects changes to $list and $listdone
      updateTodoList();
      updateDoneList();
      saveToLocalStorage();
  });

  /* The little function below clears all completed tasks
     when the '#delete' div is clicked */
  $(document).on('click', '#delete', function() {

      // makes sure $listdone is properly set
      updateDoneList();
      /* warns the user of irreversible changes
         and stores user's answer to dialog;
         such as clicking OK returns true
         and clicking Cancel returns false */
      var deleteConfirm = confirm("Deleted tasks can't be recovered.\n\nYou are going to DELETE all done tasks.");
      if (deleteConfirm == true) {
            $('.listdone').empty();
            // reflects changes to $listdone
            updateDoneList();
            saveToLocalStorage();
      } else { // do nothing
      }
  });

  /* This function marks all tasks from the "To do" list
     as completed, which moves them to the "Done" list */
  $(document).on('click', '#alldone', function() {

    // makes sure $list is properly set
    updateTodoList();
    // appends all the items included in the $list var to the "Done list"
    $('.listdone').append($list);
    // reflects changes to $listdone content
    updateDoneList();
    updateTodoList();
    /* adds class '.itemdone' to all these items
       newly added to the "Done list" */
    $('.item').addClass('itemdone');
    // removes all items from the "Todo list"
    $('.list').empty();
    // reflects changes to $list content
    updateTodoList();
    updateDoneList();
    saveToLocalStorage();
  });

  /* This function "unchecks" all tasks from the "Done" list
     and moves them back to the "To do" list */
  $(document).on('click', '#undo', function() {

    // makes sure $listdone is properly set
    updateDoneList();
    // appends all the items included in the $listdone var to the "To do" list
    $('.list').append($listdone);
    /* removes class '.itemdone' from all these items
       newly added to the "To do" list */
    $('.itemdone').removeClass('itemdone');
    // reflects changes to $list content
    updateTodoList();
    // removes all items from the "Done" list
    $('.listdone').empty();
    // reflects changes to $listdone content
    updateDoneList();
    saveToLocalStorage();
  });

  // makes list items sortable with jQuery UI
      $('ul').sortable({
          /* reflects changes to $list and $listdone
             when dragging is finished, as explained in
             http://stackoverflow.com/a/26192283/5139147
             except that I don't need "start:" here
             or it would trigger two updates,
             one of them being useless.
             I choose to update the lists when items are dragged
             so that the result of re-ordering is saved in the variables.
             It will be useful for localStorage retrieval. */
          update: function() {
                      updateTodoList();
                      updateDoneList();
                      saveToLocalStorage();
                  },
      });

    function updateTodoList () { // updates the $list global variable
        $list = $('.list').html();
        // prints out the complete $list if needed (uncomment for debugging sessions)
        console.log("$list is: ",$list);
    };

    function updateDoneList () { // updates the $listdone global variable
        $listdone = $('.listdone').html();
        // prints out the complete $listdone if needed (uncomment for debugging sessions)
        console.log("$listdone is: ",$listdone);
    };

    function saveToLocalStorage() {
        localStorage.setItem ('todolist', $list);
        // prints out localStorage for todolist
        console.log("localStorage item 'todolist' is:",localStorage.getItem ('todolist'));

        localStorage.setItem ('donelist', $listdone);
        // prints out localStorage for donelist
        console.log("localStorage item 'donelist' is:",localStorage.getItem ('donelist'));
    };

    // Clicking the "Hide Done" button toggles display
    $('#show-done-checkbox').change( function() {
      $('#donelist').toggle();
    });

    $('#show-tooltips-checkbox').change( function () {
      console.log("#show-tooltips-checkbox has changed");
      if ( $('#show-tooltips-checkbox').is(':checked')) {
        console.log("checkbox is now checked");
      } else { console.log("checkbox is now UNCHECKED");
      }
      updateTooltipsContent();
    });

    // Clicking the "Panel" button (#togglePanel) toggles right panel visibility
    $('.settings-button').click( function() {
      $('#rightpanel').toggle('slide', { direction: 'right'}, 200);
      $('.settings-button-text').toggleClass('settings-button-text-active');
      $('.hamburger-menu').toggleClass('hamburger-menu-active');
    });

    // Gives the ability to resize the todolist and donelist divs
    // (because I added the "resizable-list" class to these two divs
    $('.resizable').resizable({
      helper: "resizing-helper" // adds class for CSS
    });

});

// END "SCRIPT.JS" file
<commit_msg>Add code for Shift Click option in Settings<commit_after>// BEGIN SCRIPT.JS FILE
//
    // stores content (html, including li tags) of the "To do" list
    var $list = $('.list').html();
    // stores content (html, including li tags) of the "Done" list
    var $listdone = $('.listdone').html();

    var itemTooltip = "Click to mark the task as completed";
    var itemdoneTooltip = "Click to move the task back to the To Do list, Shift-Click to DELETE the task";
    var alldoneTooltip = "Click to mark all tasks as completed";
    var undoTooltip = "Click to move all tasks back to the To do list";
    var deleteTooltip = "Click to DELETE all completed tasks";

    var shiftClickDeletesCompletedItem = true;

$(document).ready( function() {

    var $list = localStorage.getItem('todolist');
    var $listdone = localStorage.getItem('donelist');
    console.log("after localStorage call, $list is:", $list);
    console.log("after localStorage recall, $listdone is", $listdone);

    updateTooltipsContent();

    // displays the content of "To do" and "Done" lists as recalled from LocalStorage
    $('.list').val($list);
    $('.listdone').val($listdone);
    if ( $list == undefined) {
        // do nothing
        }
    else {
        document.querySelector('.list').innerHTML = $list;
    }

    if ( $listdone == undefined) {
        // do nothing
    } else {
        document.querySelector('.listdone').innerHTML = $listdone;
    }

  // gives focus to input text box as soon document is ready
  $('#newTaskTextInput').focus();

  // Uncomment the following block to print helpful message(s) to the console (useful for DEBUG)
/*
    console.log("DEBUT TEST DEBUG");
    console.log("When page is first loaded, text input= " + $('input[name=newTaskTextInput]').val());
*/

  /* will fix this bug:
     "when user hits enter key, the page is reloaded"
     with the help of http://stackoverflow.com/a/15488912 */
  $('form').on('submit', function(event) {
      event.preventDefault();
  });

  //watches for the click event on div '#add'
  $('#add').click( function() {
      addNewTask();
  });

  if ( $('#show-tooltips-checkbox').is(':checked') ) {
    console.log("#show-tooltips-checkbox is checked");
  } else {
    console.log("#show-tooltips-checkbox is NOT checked");
  }

  function updateTooltipsContent () {
    if ( $('#show-tooltips-checkbox').is(':checked') ) {
      itemTooltip = "Click to mark the task as completed";
      itemdoneTooltip = "Click to move the task back to the To Do list, Shift-Click to DELETE the task";
      alldoneTooltip = "Click to mark all tasks as completed";
      undoTooltip = "Click to move all tasks back to the To do list";
      deleteTooltip = "Click to DELETE all completed tasks";
    } else {
      itemTooltip = "";
      itemdoneTooltip = "";
      alldoneTooltip = "";
      undoTooltip = "";
      deleteTooltip = "";
    }

    $('.item').prop('title', itemTooltip);
    $('.itemdone').prop('title', itemdoneTooltip);
    $('#alldone').prop('title', alldoneTooltip);
    $('#undo').prop('title', undoTooltip);
    $('#delete').prop('title', deleteTooltip);

  };

  /* === FUNCTION TO HANDLE WHAT HAPPENS WHEN YOU CLICK THE ADD "BUTTON"
   Basically, when '#add' button is clicked, the function gets the text
   typed in the input text box whose name is '#newTaskTextInput'
   and appends a new "listItem" list entry (li) within the "todoList" div */

    function addNewTask () {

    // Uncomment the following block to print helpful messages to the console (useful for DEBUG)
/*
       console.log();
       console.log("NOW ENTERING THE addNewTask FUNCTION");
       console.log("input[name=newTaskTextInput] contains the following string '" + $('input[name=newTaskTextInput]').val() + "'");
*/

        //stores text from text input into the itemText variable
        var itemText = $('input[name=newTaskTextInput]').val();

        // Uncomment the following block to print helpful messages to the console (useful for DEBUG)
/*
           console.log("Text entered in the input box is passed to variable itemText.");
           console.log("Now, itemText variable contains '" + itemText + "'");
*/

        /* condition to detect if the user has not modified the default textinput value
           (i.e. still displaying 'enter new task here')
           or if user has clicked (given focus to) text input but not typed text yet;
           ideal case would be to check for any number of white spaces with a regex */
        if(itemText == '' || itemText == ' ' || itemText == '  ' || itemText == 'enter new task here') {
        /* prevents from adding a new task if "#add" div is clicked
           whereas nothing has been typed in the input text box;
           in this case, there is no subsequent action except displaying an error message
           that is appended to the ".error-message" class (to control formatting with CSS) */

          // Uncomment the following line(s) to print helpful message(s) to the console (useful for DEBUG)
//           console.log("No significant text in input box, so displaying error message");

          // actually displays the error message
          $('.error-message').append('Please enter a new task before clicking the add button!
');
        }

        else { // user has actually typed text in the newTaskTextInput field;

            // Uncomment the following block to print helpful message to the console (useful for DEBUG)
/*
            console.log();
            console.log("User has entered text in input box");
            console.log("input[name=newTaskTextInput] contains the following string '" + $('input[name=newTaskTextInput]').val() + "'");
            console.log("Text entered is stored in itemText variable");
            console.log("itemText now contains: ' " + itemText +  "'");
*/

          //appends a new list item, containing the text typed by the user, to the '.list' div
          $('.list').append('
 ' + itemText + '
');

          // Uncomment the following line to print helpful message(s) to the console (useful for DEBUG)
//        console.log("This text from the itemText variable has been added to .list div");

          // empties the '.error-message' div because a new task was actually created
          $('.error-message').empty();

          // Uncomment the following line to print helpful message(s) to the console (useful for DEBUG)
 //          console.log("The .error-message class content has been emptied");

          // clears the input text box;
          $('input[name=newTaskTextInput]').val('');

          // Uncomment the following block to print helpful message to the console (useful for DEBUG)
/*
             console.log("We're at end of the addNewTask function, and itemText variable = " + itemText);
             console.log("And input[name=newTaskTextInput] contains '" + $('input[name=newTaskTextInput]').val() + "'");
*/

        };

        // reflect changes to $list and $listdone and saves them to LocalStorage
        updateTodoList();
        updateDoneList();
        saveToLocalStorage();

        return false; // makes sure pressing Enter will not reload the page
    };

  // === END OF FUNCTION HANDLING NEW TASKS ===

  /* The little function below toggles 'itemdone' class
     on items (from the '.list') that are clicked
     so that the item is greyed out and formatted as strikethrough;
     it also moves the item to the '.listdone' div */
  $(document).on('click', '.item', function() {
      $(this).toggleClass('itemdone');
      $(this).prop('title', itemdoneTooltip);
      // moves the done item to the 'listdone' div
      $('.listdone').append(this);
      // reflects changes to $list and $listdone
      updateTodoList();
      updateDoneList();
      saveToLocalStorage();
  });

  /* The little function below does the opposite
     e.g. moving back a "done" item to the "todo" list when clicked.
     IF SHIFT CLICK, item is DELETED. */
  $(document).on('click', '.itemdone', function(e) {
      if (e.shiftKey && shiftClickDeletesCompletedItem === true) { // if Shift-Click instead of a simple Click (and option is not disabled in Settings)
        $(this).remove();
      } else {
        $(this).removeClass('itemdone');
        $(this).prop('title', itemTooltip);
        // moves back the undone item to the 'list' div
        $('.list').append(this);
      }
      // reflects changes to $list and $listdone
      updateTodoList();
      updateDoneList();
      saveToLocalStorage();
  });

  /* The little function below clears all completed tasks
     when the '#delete' div is clicked */
  $(document).on('click', '#delete', function() {

      // makes sure $listdone is properly set
      updateDoneList();
      /* warns the user of irreversible changes
         and stores user's answer to dialog;
         such as clicking OK returns true
         and clicking Cancel returns false */
      var deleteConfirm = confirm("Deleted tasks can't be recovered.\n\nYou are going to DELETE all done tasks.");
      if (deleteConfirm == true) {
            $('.listdone').empty();
            // reflects changes to $listdone
            updateDoneList();
            saveToLocalStorage();
      } else { // do nothing
      }
  });

  /* This function marks all tasks from the "To do" list
     as completed, which moves them to the "Done" list */
  $(document).on('click', '#alldone', function() {

    // makes sure $list is properly set
    updateTodoList();
    // appends all the items included in the $list var to the "Done list"
    $('.listdone').append($list);
    // reflects changes to $listdone content
    updateDoneList();
    updateTodoList();
    /* adds class '.itemdone' to all these items
       newly added to the "Done list" */
    $('.item').addClass('itemdone');
    // removes all items from the "Todo list"
    $('.list').empty();
    // reflects changes to $list content
    updateTodoList();
    updateDoneList();
    saveToLocalStorage();
  });

  /* This function "unchecks" all tasks from the "Done" list
     and moves them back to the "To do" list */
  $(document).on('click', '#undo', function() {

    // makes sure $listdone is properly set
    updateDoneList();
    // appends all the items included in the $listdone var to the "To do" list
    $('.list').append($listdone);
    /* removes class '.itemdone' from all these items
       newly added to the "To do" list */
    $('.itemdone').removeClass('itemdone');
    // reflects changes to $list content
    updateTodoList();
    // removes all items from the "Done" list
    $('.listdone').empty();
    // reflects changes to $listdone content
    updateDoneList();
    saveToLocalStorage();
  });

  // makes list items sortable with jQuery UI
      $('ul').sortable({
          /* reflects changes to $list and $listdone
             when dragging is finished, as explained in
             http://stackoverflow.com/a/26192283/5139147
             except that I don't need "start:" here
             or it would trigger two updates,
             one of them being useless.
             I choose to update the lists when items are dragged
             so that the result of re-ordering is saved in the variables.
             It will be useful for localStorage retrieval. */
          update: function() {
                      updateTodoList();
                      updateDoneList();
                      saveToLocalStorage();
                  },
      });

    function updateTodoList () { // updates the $list global variable
        $list = $('.list').html();
        // prints out the complete $list if needed (uncomment for debugging sessions)
        console.log("$list is: ",$list);
    };

    function updateDoneList () { // updates the $listdone global variable
        $listdone = $('.listdone').html();
        // prints out the complete $listdone if needed (uncomment for debugging sessions)
        console.log("$listdone is: ",$listdone);
    };

    function saveToLocalStorage() {
        localStorage.setItem ('todolist', $list);
        // prints out localStorage for todolist
        console.log("localStorage item 'todolist' is:",localStorage.getItem ('todolist'));

        localStorage.setItem ('donelist', $listdone);
        // prints out localStorage for donelist
        console.log("localStorage item 'donelist' is:",localStorage.getItem ('donelist'));
    };

    // Clicking the "Hide Done" button toggles display
    $('#show-done-checkbox').change( function() {
      $('#donelist').toggle();
    });

    // Handles clicks on the "Show Tooltips" checkbox in Settings
    $('#show-tooltips-checkbox').change( function () {
      console.log("#show-tooltips-checkbox has changed");
      if ( $('#show-tooltips-checkbox').is(':checked')) {
        console.log("checkbox is now checked");
      } else { console.log("checkbox is now UNCHECKED");
      }
      updateTooltipsContent();
    });

    // Turns on/off the "Shift-Click to delete" behavior
    $('#shiftclick-delete-checkbox').change ( function() {
        console.log("#shiftclick-delete-checkbox has changed");
        shiftClickDeletesCompletedItem = !shiftClickDeletesCompletedItem;
        console.log("var shiftClickDeletesCompletedItem = " + shiftClickDeletesCompletedItem);
        if ( $('#shiftclick-delete-checkbox').is(':checked')) {
            console.log("#shiftclick-delete-checkbox is now Checked");
        } else {
            console.log("#shiftclick-delete-checkbox is now UNchecked");
        }
    });

    $('.settings-button').click( function() {
      $('#rightpanel').toggle('slide', { direction: 'right'}, 200);
      $('.settings-button-text').toggleClass('settings-button-text-active');
      $('.hamburger-menu').toggleClass('hamburger-menu-active');
    });

    // Gives the ability to resize the todolist and donelist divs
    // (because I added the "resizable-list" class to these two divs
    $('.resizable').resizable({
      helper: "resizing-helper" // adds class for CSS
    });

});

// END "SCRIPT.JS" file
<|endoftext|>


<commit_before>// ==UserScript==
// @name          PHPBB thread mute
// @namespace     http://pirati.cz/
// @version       0.24
// @author        Lukas -krtek.net- Novy <zmrd@krtek.net>
// @description   Delists muted threads for 7 days
// @match         https://forum.pirati.cz/*
// @require       http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.js
// @require       http://coruvar.cz/jquery.cookie.js
// ==/UserScript==

$(function () {
	var muted = $.cookie('mute-threads');
	if (!muted) {
		muted = {};
	} else {
		muted = JSON.parse(muted);
	}
	var now = new Date().valueOf() / 1000;
	var n = {};
	for (var i in muted) {
		var e = muted[i];
		if (now - e < 60 * 60 * 24 * 14) {
			n[i] = e;
		}
	}
	muted = n;

	$('ul.topiclist.topics li dl dt a.topictitle').each(function() {
		var url = this.href;

		url = url.replace(/\.html$/, '');
		var topic_id = url.replace(/.+t/g, '');

		if ('t-' + topic_id in muted) {
			$(this).parent().parent().parent().remove();
		} else {
			var button = $("<button>MUTE</button>");
			button.click(function() {
				muted['t-' + topic_id] = now;
				alert(muted);
				$.cookie('mute-threads', JSON.stringify(muted), {expires: 365, path: '/'});
				$(this).parent().parent().parent().remove();
			});
			button.css("font-size", "50%");
			button.css("font-weight", "bold");
			button.css("border", "1px solid gray");
			button.css("margin", "1px 10px 0 0");
			button.css("float", "right");
			button.css("background", "rgb(230,230,230)");
			button.css("color", "blue");
			button.css("border-radius", "3px");
			button.css("padding", "0px 2px");
			button.css("cursor", "pointer");
			$(this).parent().append(button);
		}
	});
})();
<commit_msg>Forgoten alert()<commit_after>// ==UserScript==
// @name          PHPBB thread mute
// @namespace     http://pirati.cz/
// @version       0.24
// @author        Lukas -krtek.net- Novy <zmrd@krtek.net>
// @description   Delists muted threads for 7 days
// @match         https://forum.pirati.cz/*
// @require       http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.js
// @require       http://coruvar.cz/jquery.cookie.js
// ==/UserScript==

$(function () {
	var muted = $.cookie('mute-threads');
	if (!muted) {
		muted = {};
	} else {
		muted = JSON.parse(muted);
	}
	var now = new Date().valueOf() / 1000;
	var n = {};
	for (var i in muted) {
		var e = muted[i];
		if (now - e < 60 * 60 * 24 * 14) {
			n[i] = e;
		}
	}
	muted = n;

	$('ul.topiclist.topics li dl dt a.topictitle').each(function() {
		var url = this.href;

		url = url.replace(/\.html$/, '');
		var topic_id = url.replace(/.+t/g, '');

		if ('t-' + topic_id in muted) {
			$(this).parent().parent().parent().remove();
		} else {
			var button = $("<button>MUTE</button>");
			button.click(function() {
				muted['t-' + topic_id] = now;
				$.cookie('mute-threads', JSON.stringify(muted), {expires: 365, path: '/'});
				$(this).parent().parent().parent().remove();
			});
			button.css("font-size", "50%");
			button.css("font-weight", "bold");
			button.css("border", "1px solid gray");
			button.css("margin", "1px 10px 0 0");
			button.css("float", "right");
			button.css("background", "rgb(230,230,230)");
			button.css("color", "blue");
			button.css("border-radius", "3px");
			button.css("padding", "0px 2px");
			button.css("cursor", "pointer");
			$(this).parent().append(button);
		}
	});
})();
<|endoftext|>


<commit_before>import PropTypes from 'prop-types';
import {
    findModel,
} from 'mobx-decorated-models';
import invariant from 'invariant';
import {
    isEmpty, isNil, find, extend, assign, pick, map,
} from 'lodash';

import { action, observable, computed } from 'mobx';

import pluralize from 'pluralize';

import Sync from './sync';
import Config from '../config';
import { toSentence, humanize } from '../lib/util';
import ModelCollection from './collection';

export {
    identifiedBy, belongsTo, hasMany,
} from 'mobx-decorated-models';
export {
    action, autorun,
} from 'mobx';
export {
    field, session, identifier,
} from './decorators';

export {
    observable, computed,
};

export class BaseModel {

    static get propType() {
        return PropTypes.instanceOf(this);
    }

    static findDerived(name) {
        return findModel(name);
    }

    static get assignableKeys() {
        return this.$schema.keys();
    }

    static get propertyOptions() {
        const options = {};
        this.$schema.forEach((val, name) => (options[name] = val.options));
        return options;
    }

    static get syncUrl() {
        invariant(this.identifiedBy, 'must have an identifiedBy property in order to calulate syncUrl');
        return `${Config.api_path}/${pluralize(this.identifiedBy)}`;
    }

    static get identifierName() {
        const field = find(this.$schema.values(), { type: 'identifier' });
        invariant(field, 'identifierName called on a model that has not designated one with `@identifier`');
        return field.name;
    }

    static get Collection() {
        return this.$collection || (this.$collection = new ModelCollection(this));
    }

    constructor(attrs) {
        if (!isEmpty(attrs)) { this.set(attrs); }
    }

    @observable syncInProgress = false;
    @observable lastServerMessage = '';
    @observable errors = {};

    @computed get errorMessage() {
        return toSentence(map(this.errors, (v, k) => `${humanize(k)} ${v}`));
    }

    @computed get isValid() {
        return isNil(this.errors) || isEmpty(this.errors);
    }

    @computed get isNew() {
        return isNil(this.identifier);
    }

    @computed get identifier() {
        return this[this.constructor.identifierName];
    }

    @computed get syncUrl() {
        const path = this.constructor.syncUrl;
        return this.isNew ? path : `${path}/${this.identifier}`;
    }

    set(attrs = {}) {
        assign(this, pick(attrs, this.constructor.assignableKeys));
        return this;
    }

    @action reset() {
        this.constructor.assignableKeys.forEach(k => (this[k] = null));
    }

    set syncData(data) {
        return this.update(data);
    }

    @computed
    get syncData() {
        return this.serialize();
    }

    fetch(options = {}) {
        invariant((!this.isNew || options.query),
                  'Unable to fetch record without it’s identifier being set or a query');
        const fetchOptions = extend(options, { limit: 1, method: 'GET' });
        if (!fetchOptions.query) {
            fetchOptions.query = { [`${this.constructor.identifierName}`]: this.identifier };
        }
        return Sync.forModel(this, fetchOptions);
    }

    save(options = {}) {
        return Sync.forModel(this, options);
    }

    destroy(options = {}) {
        return Sync.forModel(this, extend(options, { action: 'destroy' }));
    }
}

export default BaseModel;
<commit_msg>errors and deal with null data<commit_after>import PropTypes from 'prop-types';
import {
    findModel,
} from 'mobx-decorated-models';
import invariant from 'invariant';
import {
    isEmpty, isNil, find, extend, assign, pick, map, isArray,
} from 'lodash';

import { action, observable, computed } from 'mobx';

import pluralize from 'pluralize';

import Sync from './sync';
import Config from '../config';
import { toSentence, humanize } from '../lib/util';
import ModelCollection from './collection';

export {
    identifiedBy, belongsTo, hasMany,
} from 'mobx-decorated-models';
export {
    action, autorun,
} from 'mobx';
export {
    field, session, identifier,
} from './decorators';

export {
    observable, computed,
};

export class BaseModel {

    static get propType() {
        return PropTypes.instanceOf(this);
    }

    static findDerived(name) {
        return findModel(name);
    }

    static get assignableKeys() {
        return this.$schema.keys();
    }

    static get propertyOptions() {
        const options = {};
        this.$schema.forEach((val, name) => (options[name] = val.options));
        return options;
    }

    static get syncUrl() {
        invariant(this.identifiedBy, 'must have an identifiedBy property in order to calulate syncUrl');
        return `${Config.api_path}/${pluralize(this.identifiedBy)}`;
    }

    static get identifierName() {
        const field = find(this.$schema.values(), { type: 'identifier' });
        invariant(field, 'identifierName called on a model that has not designated one with `@identifier`');
        return field.name;
    }

    static get Collection() {
        return this.$collection || (this.$collection = new ModelCollection(this));
    }

    constructor(attrs) {
        if (!isEmpty(attrs)) { this.set(attrs); }
    }

    get isModel() { return true; }

    @observable syncInProgress = false;
    @observable lastServerMessage = '';
    @observable errors = {};

    @computed get errorMessage() {
        return this.errors ? toSentence(map(this.errors, (v, k) => `${humanize(k)} ${v}`)) : '';
    }

    @computed get isValid() {
        return isNil(this.errors) || isEmpty(this.errors);
    }

    @computed get isNew() {
        return isNil(this.identifier);
    }

    @computed get identifier() {
        return this[this.constructor.identifierName];
    }

    @computed get syncUrl() {
        const path = this.constructor.syncUrl;
        return this.isNew ? path : `${path}/${this.identifier}`;
    }

    set(attrs = {}) {
        assign(this, pick(attrs, this.constructor.assignableKeys));
        return this;
    }

    @action reset() {
        this.constructor.assignableKeys.forEach(k => (this[k] = null));
    }

    set syncData(data) {
        if (!data) { return this; }
        if (isArray(data) && data.length) {
            this.update(data[0]);
        } else {
            this.update(data);
        }
        return this;
    }

    @computed
    get syncData() {
        return this.serialize();
    }

    fetch(options = {}) {
        invariant((!this.isNew || options.query),
                  'Unable to fetch record without it’s identifier being set or a query');
        const fetchOptions = extend(options, { limit: 1, method: 'GET' });
        if (!fetchOptions.query) {
            fetchOptions.query = { [`${this.constructor.identifierName}`]: this.identifier };
        }
        return Sync.forModel(this, fetchOptions);
    }

    save(options = {}) {
        return Sync.forModel(this, options);
    }

    destroy(options = {}) {
        return Sync.forModel(this, extend(options, { action: 'destroy' }));
    }
}

export default BaseModel;
<|endoftext|>