<commit_before>const github = require('../services/github')
const jira = require('../services/jira')

const WORKFLOW = ['Backlog', 'In Development', 'Code Review', 'QA Review']
const ASSIGNEE_MAP = {
  'ShinyChang': 'shiny.chang',
  'Rhadow': 'howard.chang',
  'albertojg': 'alberto.gosal',
  'wmyers': 'william.myers',
  'jessinca': 'jessinca.jong',
  'rnel': 'arnel.aguinaldo',
  'wangchou': 'wangchou.lu',
  'kidwm': 'chen.heng',
}

const extendJiraFields = raw => {
  let status = 'Backlog'
  if (raw.merged) {
    status = 'QA Review'
  } else if (raw.state === 'open') {
    status = 'Code Review'
  }
  const jira = {
    issueKey: raw.title.match(/CWEB-\d+/)[0],
    fixVersion: raw.milestone && raw.milestone.match(/((\d+\.)+\d+)/)[0],
    status,
    assignee: raw.user.login
  };
  raw.jira = jira
  return raw
}

const syncGitHubToJira = () => {
  return github.getRecentPRs().then(prs => {
    return prs.map(extendJiraFields)
  }).then(prs => {
    const prPromises = prs.map(pr => {
      const {
        issueKey,
        fixVersion,
        status,
        assignee
      } = pr.jira
      return jira.getIssue(issueKey).then(issue => {
        const actionPromises = []
        if (fixVersion && !issue.fields.fixVersion) {
          actionPromises.push(jira.setFixVersion(issue.key, fixVersion))
        }
        if (status) {
          const currentStage = WORKFLOW.indexOf(issue.fields.status)
          const expectStage = WORKFLOW.indexOf(status)
          if (currentStage !== -1) {
            for (var i = currentStage + 1; i <= expectStage; i++) {
              if (WORKFLOW[i] === 'Code Review') {
                actionPromises.push(jira.transitionTo(issue.key, WORKFLOW[i]).then(reply => {
                  return jira.setAssignee(issue.key, ASSIGNEE_MAP[assignee]).then(() => {
                    return reply
                  })
                }))
              } else {
                actionPromises.push(jira.transitionTo(issue.key, WORKFLOW[i]))
              }
            }
          }
        }

        if (ASSIGNEE_MAP[assignee] !== issue.fields.assignee.key) {
          actionPromises.push(jira.setAssignee(issue.key, ASSIGNEE_MAP[assignee]))
        }

        if (!pr.assignees) {
          actionPromises.push(github.addAssignees(pr.number, assignee))
        }

        if (actionPromises.length) {
          actionPromises.unshift(Promise.resolve(`
${issueKey}: ${issue.fields.summary}
> PR: ${pr.url}
> Jira: https://honestbee.atlassian.net/browse/${issueKey}
actions:
`))
        }
        return Promise.all(actionPromises)
      }).then(res => {
        return res.filter(r => !!r).join('\n')
      })
    })
    return Promise.all(prPromises)
  }).then(res => {
    return res.filter(r => !!r).join('\n\n')
  }).catch(err => console.log(err))
}

module.exports = syncGitHubToJira
<commit_msg>Fix github assignee bug<commit_after>const github = require('../services/github')
const jira = require('../services/jira')

const WORKFLOW = ['Backlog', 'In Development', 'Code Review', 'QA Review']
const ASSIGNEE_MAP = {
  'ShinyChang': 'shiny.chang',
  'Rhadow': 'howard.chang',
  'albertojg': 'alberto.gosal',
  'wmyers': 'william.myers',
  'jessinca': 'jessinca.jong',
  'rnel': 'arnel.aguinaldo',
  'wangchou': 'wangchou.lu',
  'kidwm': 'chen.heng',
}

const extendJiraFields = raw => {
  let status = 'Backlog'
  if (raw.merged) {
    status = 'QA Review'
  } else if (raw.state === 'open') {
    status = 'Code Review'
  }
  const jira = {
    issueKey: raw.title.match(/CWEB-\d+/)[0],
    fixVersion: raw.milestone && raw.milestone.match(/((\d+\.)+\d+)/)[0],
    status,
    assignee: raw.user.login
  };
  raw.jira = jira
  return raw
}

const syncGitHubToJira = () => {
  return github.getRecentPRs().then(prs => {
    return prs.map(extendJiraFields)
  }).then(prs => {
    const prPromises = prs.map(pr => {
      const {
        issueKey,
        fixVersion,
        status,
        assignee
      } = pr.jira
      return jira.getIssue(issueKey).then(issue => {
        const actionPromises = []
        if (fixVersion && !issue.fields.fixVersion) {
          actionPromises.push(jira.setFixVersion(issue.key, fixVersion))
        }
        if (status) {
          const currentStage = WORKFLOW.indexOf(issue.fields.status)
          const expectStage = WORKFLOW.indexOf(status)
          if (currentStage !== -1) {
            for (var i = currentStage + 1; i <= expectStage; i++) {
              if (WORKFLOW[i] === 'Code Review') {
                actionPromises.push(jira.transitionTo(issue.key, WORKFLOW[i]).then(reply => {
                  return jira.setAssignee(issue.key, ASSIGNEE_MAP[assignee]).then(() => {
                    return reply
                  })
                }))
              } else {
                actionPromises.push(jira.transitionTo(issue.key, WORKFLOW[i]))
              }
            }
          }
        }

        if (ASSIGNEE_MAP[assignee] !== issue.fields.assignee.key) {
          actionPromises.push(jira.setAssignee(issue.key, ASSIGNEE_MAP[assignee]))
        }

        if (!pr.assignees.length) {
          actionPromises.push(github.addAssignees(pr.number, assignee))
        }

        if (actionPromises.length) {
          actionPromises.unshift(Promise.resolve(`
${issueKey}: ${issue.fields.summary}
> PR: ${pr.url}
> Jira: https://honestbee.atlassian.net/browse/${issueKey}
actions:
`))
        }
        return Promise.all(actionPromises)
      }).then(res => {
        return res.filter(r => !!r).join('\n')
      })
    })
    return Promise.all(prPromises)
  }).then(res => {
    return res.filter(r => !!r).join('\n\n')
  }).catch(err => console.log(err))
}

module.exports = syncGitHubToJira
<|endoftext|>


<commit_before>addEventListener('DOMContentLoaded', initCanvas);
var click;
function initCanvas() {
	var canvas = document.getElementById('screen');
	var ctx = canvas.getContext('2d');

	canvas.addEventListener('mousemove', function(evt){
		var position = getMousePos(canvas, evt);
		console.log('MOVINx:' + position.x + ' , y:' + position.y);
		if (click===true){
			draw(ctx, position.x, position.y);
		}
	});
	canvas.addEventListener('mousedown', function(evt){
		var position = getMousePos(canvas, evt);
		draw(ctx, position.x, position.y);
		click = true;
		al(click);
	});
	canvas.addEventListener('mouseup', function(evt){
		click = false;
		al(click);
	});
}

function al(click){
	if (click===true) {
		return true;
	} else {
		return false;
	}
}

function draw(ctx, positionX, positionY) {
		console.log('CLICKx:' + positionX + ' , y:' + positionY);
		ctx.beginPath();
		ctx.moveTo(positionX, positionY-1);
		ctx.lineTo (positionX, positionY);
		ctx.closePath();
		ctx.strokeStyle = "#000000";
		ctx.stroke();

}
function getMousePos(canvas, evt) { // get canvas and event to return the cordinates
    var rect = canvas.getBoundingClientRect();

    return {
      x: evt.clientX - rect.left,
      y: evt.clientY - rect.top
    };
}
<commit_msg>Update builder.js<commit_after>addEventListener('DOMContentLoaded', initCanvas);
addEventListener('DOMContentLoaded', function(){
	var canvas = document.getElementById('screen');
	var sideLeft = document.getElementById('side-left');
	var sideRight = document.getElementById('side-right');
	var top = document.getElementById('header');
	var bottom = document.getElementById('bottom');

	canvas.setAttribute("width", screen.width-90);
	canvas.setAttribute("height", screen.height-200);
})
var click;
function initCanvas() {
	var canvas = document.getElementById('screen');
	var ctx = canvas.getContext('2d');

	canvas.addEventListener('mousemove', function(evt){
		var position = getMousePos(canvas, evt);
		//console.log('MOVINx:' + position.x + ' , y:' + position.y);
		if (click===true){
			draw(ctx, position.x, position.y);
		}
	});
	canvas.addEventListener('mousedown', function(evt){
		var position = getMousePos(canvas, evt);
		draw(ctx, position.x, position.y);
		click = true;
	});
	canvas.addEventListener('mouseup', function(evt){
		click = false;
	});
}

function draw(ctx, positionX, positionY) {
		console.log('CLICKx:' + positionX + ' , y:' + positionY);
		ctx.lineWidth = '10';
		ctx.beginPath();
		ctx.arc(positionX, positionY, 3, 0, 2 * Math.PI, false);
		ctx.fill();
		ctx.stroke();
		ctx.closePath();

}
function getMousePos(canvas, evt) { // get canvas and event to return the cordinates
    var rect = canvas.getBoundingClientRect();

    return {
      x: evt.clientX - rect.left,
      y: evt.clientY - rect.top
    };
}
<|endoftext|>


<commit_before>/**
 * A steady drip of Khan Academy culture, straight into HipChat's veins.
 *
 * "@CultureCow culture us" to get your hit of culture on demand.
 */

var culturalMsgs = [
    "Shipping beats perfection.",
    "Iterate on ideas before diving into code. Design sprints are being organized by Jason and others and work well for both huge projects and small features.",
    "Concerned about the hacks in our code? Upset that the product's not good enough? Have no fear, we're getting better fast. Our site used to look like this and it was generated by one big main.py file.",
    "Our core company values.",
    "Have anything to demo at the next Monday all-hands? Everything that's shipped to real users is eligible. Show off.",
    "Be open, share your work.",
    "It's ok to leave (or simply not attend) any meeting that you don't feel is important for you.",
    "Email transparency makes it easy for anybody to follow the goings on of teams they're interested in. Join any list.",
    "As a cow, I am fairly proud of having written our Career Development doc. Hard to do w/ just hooves.",
    "Keep learning — we'll send you to conferences to do so.",
    "Anybody can fix anything. (And anybody can run a FIXIT for themselves or their team.)",
    "Wading into some old code that's really disgusting compared to your brilliant new feature? You don't have to fix everything all at once. \"Leave it better\" and use # TODOs liberally. (Ben says he's sorry for the old code.)",
    "Wanna teach the team about anything? Spread good habits? Show off what you're building? Sign up for an Exalted Speaker slot during next Wednesday's dev sitdown.",
    "Will just leave these users stories here.",
    "Do you need anything to do your job well? Hardware? Software? Food? Coach K bobblehead for your desk? It's easy to get whatever you need, and if you have any trouble ping Kamens quickly.",
    "Shipping anything sizable? Dog food it! Schedule a dogfooding hour, ping in hipchat to gather friendly eaters of dog food, profit.",
    "What are we building? How do we think about growth? How do we think about classrooms? How quickly should we expand our content?",
    "Performance is a feature and is everyone's job. Unsure how to profile your code? No worries, just ask, we have some perf nuts on the team.",
    "Anybody can own a deploy. If you've got big changes going out, you're probably that anybody. I (Culture Cow), may one day even keep track of a deploy count leaderboard and spit it out here!",
    "Write code with your reviewer in mind.",
    "Recruiting is everyone's job. Blogging about recruiting being everyone's job is Marcia's job.",
    "\"Sure there's some bad code around, we've all seen it, hell we've all written it...If there is some smelly code out there, look for ways to make it better. Start by understanding the code, and then find ways to improve upon it.\"",
    "Code reviews can make people happy.",
    ],
    morningCultureHour = 10; // hour of morning (pacific time) at which to send

module.exports = function(robot) {

    var msPerDay = 1000 * 60 * 60 * 24,
        msgIndex = randInt(0, culturalMsgs.length - 1);

    /**
     * Send a single culture tidbit and queue up the next one.
     */
    function sendCultureMessage() {
        var urlCultureCowEmoticon = "https://dujrsrsgsd3nh.cloudfront.net/img/emoticons/6574/culture-1376771657.png",
            s = (" " +
                    culturalMsgs[msgIndex] + " Moooo.");

        robot.messageHipchat(s);

        msgIndex++;
        if (msgIndex >= culturalMsgs.length) {
            msgIndex = 0;
        }
    };

    /**
     * Send a single culture tidbit IFF it's not the weekend. Cows don't work
     * on weekends.
     */
    function sendCultureMessageIfWeekday() {
        // Ignoring timezone issues in this fxn for now, as UTC's day of week
        // will always be the same as Pacific's when this is run ~10am.
        // TODO(kamens): handle timezone issues.
        var now = new Date(),
            day = now.getUTCDay();  // 0 = Sunday, ..., 6 = Saturday
        if (day !== 0 && day !== 6) {
            sendCultureMessage();
        }
    };

    /**
     * Start dripping culture tidbits now and forever, once per day.
     */
    function startCultureDrip() {
        // Send one culture message every weekday
        setInterval(sendCultureMessageIfWeekday, msPerDay);
        // And send one right now.
        sendCultureMessageIfWeekday();
    }

    // Send one culture message whenever requested
    robot.respond(/culture us$/i, function(msg) {
        sendCultureMessage();
    });

    setTimeout(startCultureDrip, msUntilMorning());
}

/**
 * Rand int generation, inclusive of min/max
 */
function randInt(min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min;
}

/**
 * Return milliseconds between now and sometime (10/11am PST) the next morning.
 */
function msUntilMorning() {
    var now = new Date(),
        later = new Date(),
        pacificUTCOffset = 7;  // not worrying about DST for now

    later.setUTCHours(morningCultureHour + pacificUTCOffset);
    later.setUTCMinutes(0);

    if (later < now) {
        later.setUTCDate(later.getUTCDate() + 1);
    }

    return Math.abs(later - now);
}
<commit_msg>Add gittip and 'brain breaks' to culture cow<commit_after>/**
 * A steady drip of Khan Academy culture, straight into HipChat's veins.
 *
 * "@CultureCow culture us" to get your hit of culture on demand.
 */

var culturalMsgs = [
    "Shipping beats perfection.",
    "Iterate on ideas before diving into code. Design sprints are being organized by Jason and others and work well for both huge projects and small features.",
    "Concerned about the hacks in our code? Upset that the product's not good enough? Have no fear, we're getting better fast. Our site used to look like this and it was generated by one big main.py file.",
    "Our core company values.",
    "Have anything to demo at the next Monday all-hands? Everything that's shipped to real users is eligible. Show off.",
    "Be open, share your work.",
    "It's ok to leave (or simply not attend) any meeting that you don't feel is important for you.",
    "Email transparency makes it easy for anybody to follow the goings on of teams they're interested in. Join any list.",
    "As a cow, I am fairly proud of having written our Career Development doc. Hard to do w/ just hooves.",
    "Keep learning — we'll send you to conferences to do so.",
    "Anybody can fix anything. (And anybody can run a FIXIT for themselves or their team.)",
    "Wading into some old code that's really disgusting compared to your brilliant new feature? You don't have to fix everything all at once. \"Leave it better\" and use # TODOs liberally. (Ben says he's sorry for the old code.)",
    "Wanna teach the team about anything? Spread good habits? Show off what you're building? Sign up for an Exalted Speaker slot during next Wednesday's dev sitdown.",
    "Will just leave these users stories here.",
    "Do you need anything to do your job well? Hardware? Software? Food? Coach K bobblehead for your desk? It's easy to get whatever you need, and if you have any trouble ping Kamens quickly.",
    "Shipping anything sizable? Dog food it! Schedule a dogfooding hour, ping in hipchat to gather friendly eaters of dog food, profit.",
    "What are we building? How do we think about growth? How do we think about classrooms? How quickly should we expand our content?",
    "Performance is a feature and is everyone's job. Unsure how to profile your code? No worries, just ask, we have some perf nuts on the team.",
    "Anybody can own a deploy. If you've got big changes going out, you're probably that anybody. I (Culture Cow), may one day even keep track of a deploy count leaderboard and spit it out here!",
    "Write code with your reviewer in mind.",
    "Recruiting is everyone's job. Blogging about recruiting being everyone's job is Marcia's job.",
    "\"Sure there's some bad code around, we've all seen it, hell we've all written it...If there is some smelly code out there, look for ways to make it better. Start by understanding the code, and then find ways to improve upon it.\"",
    "Code reviews can make people happy.",
    "@john created a way for all KA folks to give back to open source projects that we lean on. Setup your recurring donations.",
    "Nobody needs permission to take an occasional brain break by working on a FIXIT task that's been bugging you or anything else valuable that's not on your main project's critical path.",
    ],
    morningCultureHour = 10; // hour of morning (pacific time) at which to send

module.exports = function(robot) {

    var msPerDay = 1000 * 60 * 60 * 24,
        msgIndex = randInt(0, culturalMsgs.length - 1);

    /**
     * Send a single culture tidbit and queue up the next one.
     */
    function sendCultureMessage() {
        var urlCultureCowEmoticon = "https://dujrsrsgsd3nh.cloudfront.net/img/emoticons/6574/culture-1376771657.png",
            s = (" " +
                    culturalMsgs[msgIndex] + " Moooo.");

        robot.messageHipchat(s);

        msgIndex++;
        if (msgIndex >= culturalMsgs.length) {
            msgIndex = 0;
        }
    };

    /**
     * Send a single culture tidbit IFF it's not the weekend. Cows don't work
     * on weekends.
     */
    function sendCultureMessageIfWeekday() {
        // Ignoring timezone issues in this fxn for now, as UTC's day of week
        // will always be the same as Pacific's when this is run ~10am.
        // TODO(kamens): handle timezone issues.
        var now = new Date(),
            day = now.getUTCDay();  // 0 = Sunday, ..., 6 = Saturday
        if (day !== 0 && day !== 6) {
            sendCultureMessage();
        }
    };

    /**
     * Start dripping culture tidbits now and forever, once per day.
     */
    function startCultureDrip() {
        // Send one culture message every weekday
        setInterval(sendCultureMessageIfWeekday, msPerDay);
        // And send one right now.
        sendCultureMessageIfWeekday();
    }

    // Send one culture message whenever requested
    robot.respond(/culture us$/i, function(msg) {
        sendCultureMessage();
    });

    setTimeout(startCultureDrip, msUntilMorning());
}

/**
 * Rand int generation, inclusive of min/max
 */
function randInt(min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min;
}

/**
 * Return milliseconds between now and sometime (10/11am PST) the next morning.
 */
function msUntilMorning() {
    var now = new Date(),
        later = new Date(),
        pacificUTCOffset = 7;  // not worrying about DST for now

    later.setUTCHours(morningCultureHour + pacificUTCOffset);
    later.setUTCMinutes(0);

    if (later < now) {
        later.setUTCDate(later.getUTCDate() + 1);
    }

    return Math.abs(later - now);
}
<|endoftext|>


<commit_before>var app = require('cantina')
  , mongodb = require('mongodb')
  , stringify = require('querystring').stringify;

app.conf.add({
  mongo: {
    hosts: 'localhost:27017',
    db: 'cantina'
  }
});

var conf = app.conf.get('mongo');
conf.url = buildConnectUrl();

app.mongo = null;

// Must use hooks as the connect method is
app.hook('start').add(function mongoConnect (done) {
  var client = new mongodb.MongoClient();
  client.connect(conf.url, conf.options, function (err, db) {
    if (err) return done(err);
    app.mongo = db;
    app.mongo.client = client;
    done();
  });
});

/**
 * Builds a MongoDB standard connection string, as defined at
 * http://docs.mongodb.org/manual/reference/connection-string/
 */
function buildConnectUrl () {
  var url = 'mongodb://';

  if (conf.auth && conf.auth.username && conf.auth.password) {
    url += conf.auth.username + ':' + conf.auth.password + '@';
  }
  url += Array.isArray(conf.hosts) ? conf.hosts.join(',') : conf.hosts;
  url += '/' + conf.db;
  if (Object.prototype.toString.call(conf.options) === '[object Object]') {
    url += '?' + stringify(conf.options);
  }
  return url;
}<commit_msg>Fix typo<commit_after>var app = require('cantina')
  , mongodb = require('mongodb')
  , stringify = require('querystring').stringify;

app.conf.add({
  mongo: {
    hosts: 'localhost:27017',
    db: 'cantina'
  }
});

var conf = app.conf.get('mongo');
conf.url = buildConnectUrl();

app.mongo = null;

// Must use hooks as the connect method is asynchronous
app.hook('start').add(function mongoConnect (done) {
  var client = new mongodb.MongoClient();
  client.connect(conf.url, conf.options, function (err, db) {
    if (err) return done(err);
    app.mongo = db;
    app.mongo.client = client;
    done();
  });
});

/**
 * Builds a MongoDB standard connection string, as defined at
 * http://docs.mongodb.org/manual/reference/connection-string/
 */
function buildConnectUrl () {
  var url = 'mongodb://';

  if (conf.auth && conf.auth.username && conf.auth.password) {
    url += conf.auth.username + ':' + conf.auth.password + '@';
  }
  url += Array.isArray(conf.hosts) ? conf.hosts.join(',') : conf.hosts;
  url += '/' + conf.db;
  if (Object.prototype.toString.call(conf.options) === '[object Object]') {
    url += '?' + stringify(conf.options);
  }
  return url;
}<|endoftext|>


<commit_before>import Api from './api.js'
import conf from '../../conf.js'

class Drive extends Api {
    constructor() {
        super()
        this.getSpreadsheet = this.getSpreadsheet.bind(this)
        this.getDocument = this.getDocument.bind(this)
        this.getCategories = this.getCategories.bind(this)
        this.getArticleHtml = this.getArticleHtml.bind(this)
        this.driveExportUrl = 'https://drive.google.com/uc?export=download&id='
        this.slug = this.slug.bind(this)
        this.formatDate = this.formatDate.bind(this)
    }

    getSpreadsheet(fileId) {
        return this.get(
            `https://spreadsheets.google.com/feeds/list/${fileId}/od6/public/values?alt=json`,
            {
                credentials: 'omit'
            }
        )
            .then(response => response.json())
            .catch(error => console.log('error', error))
    }

    getDocument(fileId) {
        return this.get(
            `https://docs.google.com/feeds/download/documents/export/Export?id=${fileId}&exportFormat=html`,
            {
                credentials: 'omit'
            }
        )
            .then(response => {
                return response.text()
            })
            .catch(error => console.log('error', error))
    }

    getCategories() {
        return this.getSpreadsheet(conf.dashboardId).then(sheetData => {
            let articles = {}
            let categories = {}

            sheetData.feed.entry
                .map(row => ({
                    title: row.gsx$title.$t,
                    subtitle: row.gsx$subtitle.$t,
                    image: row.gsx$image.$t,
                    category: row.gsx$category.$t,
                    postId: row.gsx$postid.$t,
                    imageId: row.gsx$imageid.$t,
                    lastUpdated: row.gsx$lastupdated.$t
                }))
                .forEach(row => {
                    let category = {}

                    let categoryId = this.slug(row.category, 'category')

                    let existingCategory = Object.values(categories).find(
                        category => category.id === categoryId
                    )

                    let article = {
                        id: row.postId,
                        title: row.title,
                        subtitle: row.subtitle,
                        imageName: row.image,
                        image: this.driveExportUrl + row.imageId,
                        categoryId,
                        lastUpdated: row.lastUpdated,
                        date: this.formatDate(row.lastUpdated),
                        uri: `/articles/${row.postId}/${this.slug(
                            row.title,
                            'article'
                        )}`
                    }

                    if (existingCategory) {
                        categories[categoryId].articles.push(row.postId)
                    } else {
                        category = {
                            id: categoryId,
                            title: row.category,
                            imageName: row.image,
                            image: this.driveExportUrl + row.imageId,
                            articles: [row.postId],
                            uri: `/categories/${categoryId}`
                        }
                        categories[categoryId] = category
                    }
                    articles[row.postId] = article
                })
            return {
                articles,
                categories
            }
        })
    }

    getArticleHtml(articleId) {
        return this.getDocument(articleId).then(doc => {
            let styleStart = ''
            let splitStyleStart = doc.split(styleStart)
            let splitStyleEnd = splitStyleStart[1].split(styleEnd)

            let htmlStart = ''
            let htmlEnd = ''
            let splitHtmlStart = splitStyleEnd[1].split(htmlStart)
            let splitHtmlStart2 = splitHtmlStart[1].split(htmlStart2)
            let htmlClass = splitHtmlStart2[0]
            let htmlStartFull = htmlStart + htmlClass + htmlStart2
            splitHtmlStart = splitStyleEnd[1].split(htmlStartFull)
            let splitHtmlEnd = splitHtmlStart[1].split(htmlEnd)
            return (
                styleStart +
                splitStyleEnd[0] +
                styleEnd +
                '
' +
                splitHtmlEnd[0] +
                '
'
            )
        })
    }

    slug(str, type = 'type') {
        str = str.replace(/^\s+|\s+$/g, '')
        str = str.toLowerCase()

        let from = 'ãàáäâẽèéëêìíïîõòóöôùúüûñç·/_,:;'
        let to = 'aaaaaeeeeeiiiiooooouuuunc------'
        for (let i = 0, l = from.length; i < l; i++) {
            str = str.replace(new RegExp(from.charAt(i), 'g'), to.charAt(i))
        }

        str = str
            .replace(/[^a-z0-9 -]/g, '')
            .replace(/\s+/g, '-')
            .replace(/-+/g, '-')

        if (str.length < 4) {
            str = type + '_' + str
        }
        return str
    }

    formatDate(lastUpdated) {
        var fullDateSplit = lastUpdated.split(' ')
        var dateSplit = fullDateSplit[0].split('/')
        var day = parseInt(dateSplit[0])
        var month = dateSplit[1]
        var year = dateSplit[2]
        var monthNames = [
            'January',
            'February',
            'March',
            'April',
            'May',
            'June',
            'July',
            'August',
            'September',
            'October',
            'November',
            'December'
        ]
        var daySuffix = 'th'
        switch (day) {
            case 1:
                daySuffix = 'st'
                break
            case 2:
                daySuffix = 'nd'
                break
            case 3:
                daySuffix = 'rd'
                break
        }
        return day + daySuffix + ' of ' + monthNames[month - 1] + ' ' + year
    }
}

export default new Drive()
<commit_msg>set relative paths<commit_after>import Api from './api.js'
import conf from '../conf.js'

class Drive extends Api {
    constructor() {
        super()
        this.getSpreadsheet = this.getSpreadsheet.bind(this)
        this.getDocument = this.getDocument.bind(this)
        this.getCategories = this.getCategories.bind(this)
        this.getArticleHtml = this.getArticleHtml.bind(this)
        this.driveExportUrl = 'https://drive.google.com/uc?export=download&id='
        this.slug = this.slug.bind(this)
        this.formatDate = this.formatDate.bind(this)
    }

    getSpreadsheet(fileId) {
        return this.get(
            `https://spreadsheets.google.com/feeds/list/${fileId}/od6/public/values?alt=json`,
            {
                credentials: 'omit'
            }
        )
            .then(response => response.json())
            .catch(error => console.log('error', error))
    }

    getDocument(fileId) {
        return this.get(
            `https://docs.google.com/feeds/download/documents/export/Export?id=${fileId}&exportFormat=html`,
            {
                credentials: 'omit'
            }
        )
            .then(response => {
                return response.text()
            })
            .catch(error => console.log('error', error))
    }

    getCategories() {
        return this.getSpreadsheet(conf.dashboardId).then(sheetData => {
            let articles = {}
            let categories = {}

            sheetData.feed.entry
                .map(row => ({
                    title: row.gsx$title.$t,
                    subtitle: row.gsx$subtitle.$t,
                    image: row.gsx$image.$t,
                    category: row.gsx$category.$t,
                    postId: row.gsx$postid.$t,
                    imageId: row.gsx$imageid.$t,
                    lastUpdated: row.gsx$lastupdated.$t
                }))
                .forEach(row => {
                    let category = {}

                    let categoryId = this.slug(row.category, 'category')

                    let existingCategory = Object.values(categories).find(
                        category => category.id === categoryId
                    )

                    let article = {
                        id: row.postId,
                        title: row.title,
                        subtitle: row.subtitle,
                        imageName: row.image,
                        image: this.driveExportUrl + row.imageId,
                        categoryId,
                        lastUpdated: row.lastUpdated,
                        date: this.formatDate(row.lastUpdated),
                        uri: `/articles/${row.postId}/${this.slug(
                            row.title,
                            'article'
                        )}`
                    }

                    if (existingCategory) {
                        categories[categoryId].articles.push(row.postId)
                    } else {
                        category = {
                            id: categoryId,
                            title: row.category,
                            imageName: row.image,
                            image: this.driveExportUrl + row.imageId,
                            articles: [row.postId],
                            uri: `/categories/${categoryId}`
                        }
                        categories[categoryId] = category
                    }
                    articles[row.postId] = article
                })
            return {
                articles,
                categories
            }
        })
    }

    getArticleHtml(articleId) {
        return this.getDocument(articleId).then(doc => {
            let styleStart = ''
            let splitStyleStart = doc.split(styleStart)
            let splitStyleEnd = splitStyleStart[1].split(styleEnd)

            let htmlStart = ''
            let htmlEnd = ''
            let splitHtmlStart = splitStyleEnd[1].split(htmlStart)
            let splitHtmlStart2 = splitHtmlStart[1].split(htmlStart2)
            let htmlClass = splitHtmlStart2[0]
            let htmlStartFull = htmlStart + htmlClass + htmlStart2
            splitHtmlStart = splitStyleEnd[1].split(htmlStartFull)
            let splitHtmlEnd = splitHtmlStart[1].split(htmlEnd)
            return (
                styleStart +
                splitStyleEnd[0] +
                styleEnd +
                '
' +
                splitHtmlEnd[0] +
                '
'
            )
        })
    }

    slug(str, type = 'type') {
        str = str.replace(/^\s+|\s+$/g, '')
        str = str.toLowerCase()

        let from = 'ãàáäâẽèéëêìíïîõòóöôùúüûñç·/_,:;'
        let to = 'aaaaaeeeeeiiiiooooouuuunc------'
        for (let i = 0, l = from.length; i < l; i++) {
            str = str.replace(new RegExp(from.charAt(i), 'g'), to.charAt(i))
        }

        str = str
            .replace(/[^a-z0-9 -]/g, '')
            .replace(/\s+/g, '-')
            .replace(/-+/g, '-')

        if (str.length < 4) {
            str = type + '_' + str
        }
        return str
    }

    formatDate(lastUpdated) {
        var fullDateSplit = lastUpdated.split(' ')
        var dateSplit = fullDateSplit[0].split('/')
        var day = parseInt(dateSplit[0])
        var month = dateSplit[1]
        var year = dateSplit[2]
        var monthNames = [
            'January',
            'February',
            'March',
            'April',
            'May',
            'June',
            'July',
            'August',
            'September',
            'October',
            'November',
            'December'
        ]
        var daySuffix = 'th'
        switch (day) {
            case 1:
                daySuffix = 'st'
                break
            case 2:
                daySuffix = 'nd'
                break
            case 3:
                daySuffix = 'rd'
                break
        }
        return day + daySuffix + ' of ' + monthNames[month - 1] + ' ' + year
    }
}

export default new Drive()
<|endoftext|>


<commit_before>addStartupFunction(enableLogin);
addStartupFunction(enableVotes);

function addStartupFunction(func) {
  if(window.addEventListener)  // W3C
    window.addEventListener("load", func, false);
  else if(window.attachEvent)  // MS
    window.attachEvent("onload", func);
}

function enableLogin() {
  var loginlink = document.getElementById("headerloginlink");
  if(loginlink)
    loginlink.onclick = showLoginForm;
  var loginform = document.getElementById("loginform");
  if(loginform) {
    var fieldset = loginform.getElementsByTagName("fieldset");
    if(fieldset.length) {
      fieldset = fieldset[0];
      formAddData(fieldset, "return", "xml");
      formAddData(fieldset, "formid", "userlogin");
      formAddData(fieldset, "website", "DO NOT CHANGE THIS");
      formAddData(fieldset, "comment", "");
    }
  }
  var loginbutton = document.getElementById("loginbutton");
  if(loginbutton)
    loginbutton.onclick = submitLogin;
  var logincancel = document.getElementById("logincancel");
  if(logincancel)
    logincancel.onclick = cancelLogin;
}

function showLoginForm() {
  var mask = document.getElementById("loginmask");
  if(mask)
    mask.style.display = "block";
  var loginform = document.getElementById("loginform");
  if(loginform) {
    loginform.style.display = "block";
    var loginfield = document.getElementById("loginfield");
    if(loginfield)
      loginfield.focus();
    return false;
  }
}

function cancelLogin() {
  if(this.form) {
    this.form.style.display = "";
    var loginmask = document.getElementById("loginmask");
    if(loginmask)
      loginmask.style.display = "";
    return false;
  }
}

function submitLogin() {
  // DO:  show some sort of waiting message
  postFormAsync(this, loginFinished, this.form);
  return false;
}
function loginFinished(req, form) {
  if(!req.responseXML || !req.responseXML.documentElement) {
    alert("Error:\n" + req.responseText);
    return;
  }
  var response = req.responseXML.documentElement;
  if(response.attributes.getNamedItem("result").value.toLowerCase() != "success") {
    var errors = "";
    for(var error = response.firstChild; error; error = error.nextSibling)
      if(error.firstChild)  // it will find the line breaks
        errors += "\n" + error.firstChild.nodeValue;
    alert("error(s) encountered casting your vote:\n" + errors);
    return;
  }
  // login worked, so reload the current page
  window.location.reload();
}

function enableVotes() {
  var divs = document.getElementsByTagName("div");
  for(var i = 0; i < divs.length; i++)
    if(divs[i].className == "rating") {
      var links = new Array();
      for(var a = divs[i].firstChild; a != null; a = a.nextSibling) {
        // DO:  highlight related links on hover
        var img = a.getElementsByTagName("img");
        if(img.length) {
          a.vote = img[0].alt;
          var vote = +a.vote;
          links[vote] = a;
          if(vote < 0)
            for(var j = -3; j < vote; j++) {
              if(!links[j].hilite)
                links[j].hilite = new Array();
              links[j].hilite[links[j].hilite.length] = a;
            }
          if(vote > 1) {
            a.hilite = new Array();
            for(var j = 1; j < vote; j++)
              a.hilite[a.hilite.length] = links[j];
          }
          a.onclick = submitVote;
          a.onmouseover = hilightOtherVotes;
          a.onmouseout = unhilightOtherVotes;
        }
      }
    }
}

function submitVote() {
  var params = new Array();
  var p = 0;
  params[p++] = "formid=vote";
  params[p++] = uriParam("vote", this.vote);
  params[p++] = uriParam("website", "DO NOT CHANGE THIS");  // this and the next are for anti-spam measures.
  params[p++] = "comment=";
  params[p++] = "return=xml";
  var msg = document.createElement("div");
  msg.appendChild(document.createTextNode("casting vote..."))
  this.parentNode.appendChild(msg);
  postAsync(this.href, voteFinished, new Array(this, msg), params);
  return false;
}
function voteFinished(req, args) {
  if(args.length != 2) {
    alert("wrong number of arguments");
    return;
  }
  var link = args[0];
  var msg = args[1];
  try {
    if(!req.responseXML || !req.responseXML.documentElement) {
      alert("Error:\n" + req.responseText);
      return;
    }
    var response = req.responseXML.documentElement;
    if(response.attributes.getNamedItem("result").value.toLowerCase() != "success") {
      var errors = "";
      for(var error = response.firstChild; error; error = error.nextSibling)
        if(error.firstChild)  // it will find the line breaks
          errors += "\n" + error.firstChild.nodeValue;
      alert("error(s) encountered casting your vote:\n" + errors);
      return;
    }
    var vote = +response.getElementsByTagName("vote")[0].firstChild.nodeValue;
    var rating = +response.getElementsByTagName("rating")[0].firstChild.nodeValue;
    var votes = +response.getElementsByTagName("votes")[0].firstChild.nodeValue;
    for(var a = link.parentNode.firstChild; a != null; a = a.nextSibling)
      if(a.nodeName == "a") {
        var src = "/images/vote/";
        if(vote < 0 && +a.vote < 0 && +a.vote >= vote || vote == 0 && +a.vote == 0 || vote > 0 && +a.vote > 0 && +a.vote <= vote)
          src += "current/";
        if(+a.vote < 0)
          src += "down";
        else if(+a.vote == 0)
          src += "none";
        else
          src += "up";
        // negative link that needs to be filled
        if(+a.vote < 0 && rating < 0 && +a.vote + 1 > rating)
          if(+a.vote >= rating)
            src += "10";
          else
            src += round((rating - Math.ceil(rating)) * -10);
        // indifferent link that needs te be filled
        if(+a.vote == 0 && rating == 0)
          src += "0";
        // positive link that needs to be filled
        if(+a.vote > 0 && rating > 0 && +a.vote - 1 < rating)
          if(+a.vote <= rating)
            src += "10";
          else
            src += round((rating - Math.floor(rating)) * 10);
        a.getElementsByTagName("img")[0].src = src + ".png";
      } else if(a.nodeName == "div" && a.firstChild.nodeValue.match(/^\([0-9]+ votes?\)$/))
        a.firstChild.nodeValue = "(" + votes + "vote" + (votes == 1 ? ")" : "s)");
  } finally {
    msg.parentNode.removeChild(msg);
  }
}

function hilightOtherVotes() {
  if(this.hilite)
    for(var i = 0; i < this.hilite.length; i++)
      this.hilite[i].className = "votepartner";
}

function unhilightOtherVotes() {
  if(this.hilite)
    for(var i = 0; i < this.hilite.length; i++)
      this.hilite[i].className = "";
}

function getAsync(url, finished, args) {
  var req = ajaxRequestObject();
  if(req == null) {
    alert("your browser supports javascript but not ajax.  please update your browser or try again with javascript off.");
    return false;
  }
  req.onreadystatechange = function() {
    if(req.readyState == 4)
      finished(req, args);
  }
  req.open("GET", url, true);
  req.send(null);
  return true;
}

function postFormAsync(submit, finished, args) {
  var params = new Array();
  var p = 0;
  var form = submit.form;
  var inputs = form.getElementsByTagName("input");
  for(var i = 0; i < inputs.length; i++)
    if(inputs[i].name && (inputs[i].type != "submit" || inputs[i] == submit) && (inputs[i].type != "checkbox" || inputs[i].checked))
      params[p++] = uriParam(inputs[i].name, inputs[i].value);
  var selects = form.getElementsByTagName("select");
  for(var i = 0; i < selects.length; i++)
    if(selects[i].name)
      params[p++] = uriParam(selects[i].name, selects[i].options[selects[i].selectedIndex].value);
  var textareas = form.getElementsByTagName("textarea");
  for(var i = 0; i < textareas.length; i++)
    if(textareas[i].name)
      params[p++] = uriParam(textareas[i].name, textareas[i].value);
  return postAsync(form.action, finished, args, params);
}

function postAsync(url, finished, args, params) {
  var req = ajaxRequestObject();
  if(req == null) {
    alert("your browser supports javascript but not ajax.  please update your browser or try again with javascript off.");
    return false;
  }
  req.onreadystatechange = function() {
    if(req.readyState == 4)
      finished(req, args);
  }
  req.open("POST", url, true);
  var data = params instanceof Array ? params.join("&") : params;
  req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
  req.send(data);
  return true;
}

function uriParam(name, value) {
  return encodeURIComponent(name) + "=" + encodeURIComponent(value);
}

function ajaxRequestObject() {
  var req = null;
  try {
    // modern browsers and internet explorer 7+
    req = new XMLHttpRequest();
  } catch(e) {
    try {
      // internet explorer 6
      req = new ActiveXObject("Msxml2.XMLHTTP");
    } catch(e) {
      try {
        // older internet explorer
        req = new ActiveXObject("Microsoft.XMLHTTP");
      } catch(e) {}  // couldn't get an http request object
    }
  }
  return req;
}

function getSelectedText(element) {
  if(element && element.setSelectionRange && typeof(element.selectionStart) != "undefined")
    return element.value.substr(element.selectionStart, element.selectionEnd - element.selectionStart);  // this gets executed in Firefox
  if(window.getSelection)  // element doesn't have its own selection
    return window.getSelection();
  if(document.selection)
    return document.selection.createRange().text;  // this gets executed in IE6
  return false;
}

function formAddData(parent, name, value) {
  var input = document.createElement("input");
  input.type = "hidden";  // IE seems to crash doing this if it's already been added to the document
  parent.appendChild(input);
  input.name = name;
  input.value = value;
}
<commit_msg>fix login error message to not talk about voting<commit_after>addStartupFunction(enableLogin);
addStartupFunction(enableVotes);

function addStartupFunction(func) {
  if(window.addEventListener)  // W3C
    window.addEventListener("load", func, false);
  else if(window.attachEvent)  // MS
    window.attachEvent("onload", func);
}

function enableLogin() {
  var loginlink = document.getElementById("headerloginlink");
  if(loginlink)
    loginlink.onclick = showLoginForm;
  var loginform = document.getElementById("loginform");
  if(loginform) {
    var fieldset = loginform.getElementsByTagName("fieldset");
    if(fieldset.length) {
      fieldset = fieldset[0];
      formAddData(fieldset, "return", "xml");
      formAddData(fieldset, "formid", "userlogin");
      formAddData(fieldset, "website", "DO NOT CHANGE THIS");
      formAddData(fieldset, "comment", "");
    }
  }
  var loginbutton = document.getElementById("loginbutton");
  if(loginbutton)
    loginbutton.onclick = submitLogin;
  var logincancel = document.getElementById("logincancel");
  if(logincancel)
    logincancel.onclick = cancelLogin;
}

function showLoginForm() {
  var mask = document.getElementById("loginmask");
  if(mask)
    mask.style.display = "block";
  var loginform = document.getElementById("loginform");
  if(loginform) {
    loginform.style.display = "block";
    var loginfield = document.getElementById("loginfield");
    if(loginfield)
      loginfield.focus();
    return false;
  }
}

function cancelLogin() {
  if(this.form) {
    this.form.style.display = "";
    var loginmask = document.getElementById("loginmask");
    if(loginmask)
      loginmask.style.display = "";
    return false;
  }
}

function submitLogin() {
  // DO:  show some sort of waiting message
  postFormAsync(this, loginFinished, this.form);
  return false;
}
function loginFinished(req, form) {
  if(!req.responseXML || !req.responseXML.documentElement) {
    alert("Error:\n" + req.responseText);
    return;
  }
  var response = req.responseXML.documentElement;
  if(response.attributes.getNamedItem("result").value.toLowerCase() != "success") {
    var errors = "";
    for(var error = response.firstChild; error; error = error.nextSibling)
      if(error.firstChild)  // it will find the line breaks
        errors += "\n" + error.firstChild.nodeValue;
    alert("login attempt failed:\n" + errors);
    return;
  }
  // login worked, so reload the current page
  window.location.reload();
}

function enableVotes() {
  var divs = document.getElementsByTagName("div");
  for(var i = 0; i < divs.length; i++)
    if(divs[i].className == "rating") {
      var links = new Array();
      for(var a = divs[i].firstChild; a != null; a = a.nextSibling) {
        // DO:  highlight related links on hover
        var img = a.getElementsByTagName("img");
        if(img.length) {
          a.vote = img[0].alt;
          var vote = +a.vote;
          links[vote] = a;
          if(vote < 0)
            for(var j = -3; j < vote; j++) {
              if(!links[j].hilite)
                links[j].hilite = new Array();
              links[j].hilite[links[j].hilite.length] = a;
            }
          if(vote > 1) {
            a.hilite = new Array();
            for(var j = 1; j < vote; j++)
              a.hilite[a.hilite.length] = links[j];
          }
          a.onclick = submitVote;
          a.onmouseover = hilightOtherVotes;
          a.onmouseout = unhilightOtherVotes;
        }
      }
    }
}

function submitVote() {
  var params = new Array();
  var p = 0;
  params[p++] = "formid=vote";
  params[p++] = uriParam("vote", this.vote);
  params[p++] = uriParam("website", "DO NOT CHANGE THIS");  // this and the next are for anti-spam measures.
  params[p++] = "comment=";
  params[p++] = "return=xml";
  var msg = document.createElement("div");
  msg.appendChild(document.createTextNode("casting vote..."))
  this.parentNode.appendChild(msg);
  postAsync(this.href, voteFinished, new Array(this, msg), params);
  return false;
}
function voteFinished(req, args) {
  if(args.length != 2) {
    alert("wrong number of arguments");
    return;
  }
  var link = args[0];
  var msg = args[1];
  try {
    if(!req.responseXML || !req.responseXML.documentElement) {
      alert("Error:\n" + req.responseText);
      return;
    }
    var response = req.responseXML.documentElement;
    if(response.attributes.getNamedItem("result").value.toLowerCase() != "success") {
      var errors = "";
      for(var error = response.firstChild; error; error = error.nextSibling)
        if(error.firstChild)  // it will find the line breaks
          errors += "\n" + error.firstChild.nodeValue;
      alert("error(s) encountered casting your vote:\n" + errors);
      return;
    }
    var vote = +response.getElementsByTagName("vote")[0].firstChild.nodeValue;
    var rating = +response.getElementsByTagName("rating")[0].firstChild.nodeValue;
    var votes = +response.getElementsByTagName("votes")[0].firstChild.nodeValue;
    for(var a = link.parentNode.firstChild; a != null; a = a.nextSibling)
      if(a.nodeName == "a") {
        var src = "/images/vote/";
        if(vote < 0 && +a.vote < 0 && +a.vote >= vote || vote == 0 && +a.vote == 0 || vote > 0 && +a.vote > 0 && +a.vote <= vote)
          src += "current/";
        if(+a.vote < 0)
          src += "down";
        else if(+a.vote == 0)
          src += "none";
        else
          src += "up";
        // negative link that needs to be filled
        if(+a.vote < 0 && rating < 0 && +a.vote + 1 > rating)
          if(+a.vote >= rating)
            src += "10";
          else
            src += round((rating - Math.ceil(rating)) * -10);
        // indifferent link that needs te be filled
        if(+a.vote == 0 && rating == 0)
          src += "0";
        // positive link that needs to be filled
        if(+a.vote > 0 && rating > 0 && +a.vote - 1 < rating)
          if(+a.vote <= rating)
            src += "10";
          else
            src += round((rating - Math.floor(rating)) * 10);
        a.getElementsByTagName("img")[0].src = src + ".png";
      } else if(a.nodeName == "div" && a.firstChild.nodeValue.match(/^\([0-9]+ votes?\)$/))
        a.firstChild.nodeValue = "(" + votes + "vote" + (votes == 1 ? ")" : "s)");
  } finally {
    msg.parentNode.removeChild(msg);
  }
}

function hilightOtherVotes() {
  if(this.hilite)
    for(var i = 0; i < this.hilite.length; i++)
      this.hilite[i].className = "votepartner";
}

function unhilightOtherVotes() {
  if(this.hilite)
    for(var i = 0; i < this.hilite.length; i++)
      this.hilite[i].className = "";
}

function getAsync(url, finished, args) {
  var req = ajaxRequestObject();
  if(req == null) {
    alert("your browser supports javascript but not ajax.  please update your browser or try again with javascript off.");
    return false;
  }
  req.onreadystatechange = function() {
    if(req.readyState == 4)
      finished(req, args);
  }
  req.open("GET", url, true);
  req.send(null);
  return true;
}

function postFormAsync(submit, finished, args) {
  var params = new Array();
  var p = 0;
  var form = submit.form;
  var inputs = form.getElementsByTagName("input");
  for(var i = 0; i < inputs.length; i++)
    if(inputs[i].name && (inputs[i].type != "submit" || inputs[i] == submit) && (inputs[i].type != "checkbox" || inputs[i].checked))
      params[p++] = uriParam(inputs[i].name, inputs[i].value);
  var selects = form.getElementsByTagName("select");
  for(var i = 0; i < selects.length; i++)
    if(selects[i].name)
      params[p++] = uriParam(selects[i].name, selects[i].options[selects[i].selectedIndex].value);
  var textareas = form.getElementsByTagName("textarea");
  for(var i = 0; i < textareas.length; i++)
    if(textareas[i].name)
      params[p++] = uriParam(textareas[i].name, textareas[i].value);
  return postAsync(form.action, finished, args, params);
}

function postAsync(url, finished, args, params) {
  var req = ajaxRequestObject();
  if(req == null) {
    alert("your browser supports javascript but not ajax.  please update your browser or try again with javascript off.");
    return false;
  }
  req.onreadystatechange = function() {
    if(req.readyState == 4)
      finished(req, args);
  }
  req.open("POST", url, true);
  var data = params instanceof Array ? params.join("&") : params;
  req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
  req.send(data);
  return true;
}

function uriParam(name, value) {
  return encodeURIComponent(name) + "=" + encodeURIComponent(value);
}

function ajaxRequestObject() {
  var req = null;
  try {
    // modern browsers and internet explorer 7+
    req = new XMLHttpRequest();
  } catch(e) {
    try {
      // internet explorer 6
      req = new ActiveXObject("Msxml2.XMLHTTP");
    } catch(e) {
      try {
        // older internet explorer
        req = new ActiveXObject("Microsoft.XMLHTTP");
      } catch(e) {}  // couldn't get an http request object
    }
  }
  return req;
}

function getSelectedText(element) {
  if(element && element.setSelectionRange && typeof(element.selectionStart) != "undefined")
    return element.value.substr(element.selectionStart, element.selectionEnd - element.selectionStart);  // this gets executed in Firefox
  if(window.getSelection)  // element doesn't have its own selection
    return window.getSelection();
  if(document.selection)
    return document.selection.createRange().text;  // this gets executed in IE6
  return false;
}

function formAddData(parent, name, value) {
  var input = document.createElement("input");
  input.type = "hidden";  // IE seems to crash doing this if it's already been added to the document
  parent.appendChild(input);
  input.name = name;
  input.value = value;
}
<|endoftext|>


<commit_before>const ghpages = require('gh-pages');
const path = require('path');
const date = new Date();

/** Directory */
const directory = path.resolve(__dirname, '/../.docz/dist');

/** Branch */
const branch = 'gh-pages';

/** Repo */
const repo = 'https://' + process.env.GH_TOKEN + '@github.com/basarat/gls.git';

ghpages.publish(directory, {
  message: `[ci skip] deployment (${date.getUTCFullYear()}-${date.getUTCMonth() + 1}-${date.getUTCDate()}-${date.getUTCHours()}-${date.getUTCMinutes()})`,
  branch: branch,
  repo: repo,
  user: {
    name: 'basarat',
    email: 'basaratali@gmail.com'
  }
}, (err) => {
  if (err) {
    console.log('--publish failed!--', err)
  } else {
    console.log('--publish done--');
  }
});<commit_msg>path issue<commit_after>const ghpages = require('gh-pages');
const path = require('path');
const date = new Date();

/** Directory */
const directory = path.resolve(__dirname, '/../.docz/dist');
console.log(__dirname, path.resolve(__dirname,'/..'));

/** Branch */
const branch = 'gh-pages';

/** Repo */
const repo = 'https://' + process.env.GH_TOKEN + '@github.com/basarat/gls.git';

ghpages.publish(directory, {
  message: `[ci skip] deployment (${date.getUTCFullYear()}-${date.getUTCMonth() + 1}-${date.getUTCDate()}-${date.getUTCHours()}-${date.getUTCMinutes()})`,
  branch: branch,
  repo: repo,
  user: {
    name: 'basarat',
    email: 'basaratali@gmail.com'
  }
}, (err) => {
  if (err) {
    console.log('--publish failed!--', err)
  } else {
    console.log('--publish done--');
  }
});<|endoftext|>


<commit_before>/*jslint browser: true*/

( function( vui ) {

	'use strict';

	// Check if the provided vui global is defined, otherwise try to require it if
	// we're in a CommonJS environment; otherwise we'll just fail out
	if( vui === undefined ) {
		if( typeof require === 'function' ) {
			vui = require('../../core');
		} else {
			throw new Error('load vui first');
		}
	}

	// Export the vui object if we're in a CommonJS environment.
	// It will already be on the window otherwise
	if( typeof module === 'object' && typeof module.exports === 'object' ) {
		module.exports = vui;
	}

	var $ = vui.$;

	$.widget( 'vui.vui_changeTracking', {

		_create: function() {

			var nodeName = this.element.prop( 'nodeName' );
			var me = this;

			if( nodeName === 'INPUT' && this.element.attr( 'type' ) === 'radio' ) {
				var groupName = this.element.prop( 'name' );
				if( groupName ) {
					var selectedValue = $( 'input[name="' + groupName + '"]:checked' ).val();
					this.element.data( 'selectedValue', selectedValue );
				}
			}

			var evtData = { me: me };

			this.tracker = this.element
				.closest( '[data-track-changes="true"]' );

			this.tracker
				.on( 'vui-reset', evtData, this._handleReset );

			this.element
				.uniqueId()
				.data( 'originalValue', me._getValue( me.element ) )
				.data( 'hasChanged', false )
				.on( 'change.vui', evtData, this._handleChange );

		},

		_destroy: function () {

			this.tracker.off( 'vui-reset', this._handleReset );

			this.element
				.trigger( 'vui-restore', { 'id': this.element.attr( 'id' ) } )
				.off( 'change.vui' )
				.removeUniqueId();

		},

		_getValue: function( $node ) {

			var node = $node.get(0);
			var nodeName = $node.prop( 'nodeName' );

			if( nodeName === 'SELECT' ) {
				return $node.val();
			}

			if( nodeName === 'INPUT' ) {

				var inputType = $node.attr( 'type' );

				if( inputType === 'checkbox' || inputType === 'radio' ) {
					return node.checked;
				}

			}

			return node.value;

		},

		_handleChange: function( evt ) {

			var me = evt.data.me;

			var $node = me.element;
			var node = $node.get(0);
			var nodeName = $node.prop( 'nodeName' );

			if( nodeName === 'INPUT' && $node.attr( 'type' ) === 'radio' && $node.prop( 'name' ) ) {

				var groupName = $node.prop( 'name' );
				$( 'input[name="' + groupName + '"]' ).each( function( i ) {

					if( this !== node ) {

						var $this = $( this );

						if ( $this.val() === $node.data( 'selectedValue' ) ) {
							me._triggerEvent( $this );
						}

						$this.data( 'selectedValue', $node.val() );
					}

				} );

				$node.data( 'selectedValue', $node.val() );

			}

			me._triggerEvent( $node );

		},

		_handleReset: function( evt ) {

			var me = evt.data.me;

			me.element
				.data( 'originalValue', me._getValue( me.element ) )
				.data( 'hasChanged', false )
				.trigger( 'vui-restore', { 'id': me.element.attr( 'id' ) } );

		},

		_triggerEvent: function( $target ) {

			var args = { 'id': $target.attr( 'id' ) };

			var hasChanged = this._getValue( $target ) !==
				$target.data( 'originalValue' );

			$target
				.data( 'hasChanged', hasChanged )
				.trigger( hasChanged ? 'vui-change' : 'vui-restore', args );

		},

		hasChanged: function() {
			return $( this.element ).data( 'hasChanged' );
		}

	} );

	vui.addClassInitializer(
			'vui-input',
			function( node ) {
				$( node ).vui_changeTracking();
			}
		);

} )( window.vui );<commit_msg>Fixing bug where radios that shared a value would trigger extra restore events. Using the ID instead, which is guaranteed to be unique, and adding some tests to handle this case.<commit_after>/*jslint browser: true*/

( function( vui ) {

	'use strict';

	// Check if the provided vui global is defined, otherwise try to require it if
	// we're in a CommonJS environment; otherwise we'll just fail out
	if( vui === undefined ) {
		if( typeof require === 'function' ) {
			vui = require('../../core');
		} else {
			throw new Error('load vui first');
		}
	}

	// Export the vui object if we're in a CommonJS environment.
	// It will already be on the window otherwise
	if( typeof module === 'object' && typeof module.exports === 'object' ) {
		module.exports = vui;
	}

	var $ = vui.$;

	$.widget( 'vui.vui_changeTracking', {

		_create: function() {

			var nodeName = this.element.prop( 'nodeName' );
			var me = this;

			if( nodeName === 'INPUT' && this.element.attr( 'type' ) === 'radio' ) {
				var groupName = this.element.prop( 'name' );
				if( groupName ) {
					var selected = $( 'input[name="' + groupName + '"]:checked' );
					selected.uniqueId();
					this.element.data( 'selectedId', selected.attr('id') );
				}
			}

			var evtData = { me: me };

			this.tracker = this.element
				.closest( '[data-track-changes="true"]' );

			this.tracker
				.on( 'vui-reset', evtData, this._handleReset );

			this.element
				.uniqueId()
				.data( 'originalValue', me._getValue( me.element ) )
				.data( 'hasChanged', false )
				.on( 'change.vui', evtData, this._handleChange );

		},

		_destroy: function () {

			this.tracker.off( 'vui-reset', this._handleReset );

			this.element
				.trigger( 'vui-restore', { 'id': this.element.attr( 'id' ) } )
				.off( 'change.vui' )
				.removeUniqueId();

		},

		_getValue: function( $node ) {

			var node = $node.get(0);
			var nodeName = $node.prop( 'nodeName' );

			if( nodeName === 'SELECT' ) {
				return $node.val();
			}

			if( nodeName === 'INPUT' ) {

				var inputType = $node.attr( 'type' );

				if( inputType === 'checkbox' || inputType === 'radio' ) {
					return node.checked;
				}

			}

			return node.value;

		},

		_handleChange: function( evt ) {

			var me = evt.data.me;

			var $node = me.element;
			var node = $node.get(0);
			var nodeName = $node.prop( 'nodeName' );
			var selectedId = $node.data( 'selectedId' );
			var id = $node.attr('id');

			if( nodeName === 'INPUT' && $node.attr( 'type' ) === 'radio' ) {

				var groupName = $node.prop( 'name' );
				if( groupName ) {

					$( 'input[name="' + groupName + '"]' ).each( function( i ) {

						var $this = $( this );

						if( this !== node && $this.attr('id') === selectedId ) {
							me._triggerEvent( $this );
						}

						$this.data( 'selectedId', id );

					} );

				}
			}

			me._triggerEvent( $node );

		},

		_handleReset: function( evt ) {

			var me = evt.data.me;

			me.element
				.data( 'originalValue', me._getValue( me.element ) )
				.data( 'hasChanged', false )
				.trigger( 'vui-restore', { 'id': me.element.attr( 'id' ) } );

		},

		_triggerEvent: function( $target ) {

			var args = { 'id': $target.attr( 'id' ) };

			var hasChanged = this._getValue( $target ) !==
				$target.data( 'originalValue' );

			$target
				.data( 'hasChanged', hasChanged )
				.trigger( hasChanged ? 'vui-change' : 'vui-restore', args );

		},

		hasChanged: function() {
			return $( this.element ).data( 'hasChanged' );
		}

	} );

	vui.addClassInitializer(
			'vui-input',
			function( node ) {
				$( node ).vui_changeTracking();
			}
		);

} )( window.vui );<|endoftext|>


<commit_before>const path = require('path');
const CSSSplitWebpackPlugin = require('css-split-webpack-plugin').default;
const OfflinePlugin = require('offline-plugin');
const replaceLib = require('antd-tools/lib/replaceLib');
const getExternalResources = require('./getExternalResources');

const isDev = process.env.NODE_ENV === 'development';
const usePreact = process.env.REACT_ENV === 'preact';

function alertBabelConfig(rules) {
  rules.forEach((rule) => {
    if (rule.loader && rule.loader === 'babel-loader') {
      if (rule.options.plugins.indexOf(replaceLib) === -1) {
        rule.options.plugins.push(replaceLib);
      }
      rule.options.plugins = rule.options.plugins.filter(plugin =>
        !plugin.indexOf || plugin.indexOf('babel-plugin-add-module-exports') === -1
      );
    } else if (rule.use) {
      alertBabelConfig(rule.use);
    }
  });
}

module.exports = {
  port: 8001,
  source: {
    components: './components',
    docs: './docs',
    changelog: [
      'CHANGELOG.zh-CN.md',
      'CHANGELOG.en-US.md',
    ],
  },
  theme: './site/theme',
  htmlTemplate: './site/theme/static/template.html',
  themeConfig: {
    categoryOrder: {
      'Ant Design': 0,
      原则: 1,
      Principles: 1,
      视觉: 2,
      Visual: 2,
      模式: 3,
      Patterns: 3,
      其他: 6,
      Other: 6,
      Components: 100,
    },
    typeOrder: {
      General: 0,
      Layout: 1,
      Navigation: 2,
      'Data Entry': 3,
      'Data Display': 4,
      Feedback: 5,
      Localization: 6,
      Other: 7,
    },
    docVersions: {
      '0.9.x': 'http://09x.ant.design',
      '0.10.x': 'http://010x.ant.design',
      '0.11.x': 'http://011x.ant.design',
      '0.12.x': 'http://012x.ant.design',
      '1.x': 'http://1x.ant.design',
      '2.x': 'http://2x.ant.design',
    },
  },
  filePathMapper(filePath) {
    if (filePath === '/index.html') {
      return ['/index.html', '/index-cn.html'];
    }
    if (filePath.endsWith('/index.html')) {
      return [filePath, filePath.replace(/\/index\.html$/, '-cn/index.html')];
    }
    if (filePath !== '/404.html' && filePath !== '/index-cn.html') {
      return [filePath, filePath.replace(/\.html$/, '-cn.html')];
    }
    return filePath;
  },
  doraConfig: {
    verbose: true,
    plugins: ['dora-plugin-upload'],
  },
  webpackConfig(config) {
    config.resolve.alias = {
      'antd/lib': path.join(process.cwd(), 'components'),
      'antd/es': path.join(process.cwd(), 'components'),
      antd: path.join(process.cwd(), 'index'),
      site: path.join(process.cwd(), 'site'),
      'react-router': 'react-router/umd/ReactRouter',
    };

    config.externals = {
      'react-router-dom': 'ReactRouterDOM',
    };

    if (usePreact) {
      config.resolve.alias = Object.assign({}, config.resolve.alias, {
        react: 'preact-compat',
        'react-dom': 'preact-compat',
        'create-react-class': 'preact-compat/lib/create-react-class',
        'react-router': 'react-router',
      });
    }

    if (isDev) {
      config.devtool = 'source-map';
    }

    alertBabelConfig(config.module.rules);

    config.plugins.push(
      new CSSSplitWebpackPlugin({ size: 4000 }),
      new OfflinePlugin({
        appShell: '/app-shell',
        caches: {
          main: [':rest:'],
          additional: [':externals:'],
        },
        externals: [
          '/app-shell',
          'https://at.alicdn.com/t/font_148784_v4ggb6wrjmkotj4i.woff',
          'https://at.alicdn.com/t/font_148784_v4ggb6wrjmkotj4i.eot',
          'https://at.alicdn.com/t/font_148784_v4ggb6wrjmkotj4i.svg',
          'https://at.alicdn.com/t/font_148784_v4ggb6wrjmkotj4i.ttf',
          'https://img.shields.io/npm/v/antd.svg?style=flat-square',
          'https://badges.gitter.im/ant-design/ant-design-english.svg',
          'https://badges.gitter.im/Join%20Chat.svg',
        ].concat(getExternalResources()),
        responseStrategy: 'network-first',
        safeToUseOptionalCaches: true,
        ServiceWorker: {
          events: true,
          prefetchRequest: {
            mode: 'no-cors',
          },
        },
        AppCache: false,
      }),
    );

    return config;
  },

  htmlTemplateExtraData: {
    isDev,
    usePreact,
  },
};
<commit_msg>Don't cache badge since they don't allow cor<commit_after>const path = require('path');
const CSSSplitWebpackPlugin = require('css-split-webpack-plugin').default;
const OfflinePlugin = require('offline-plugin');
const replaceLib = require('antd-tools/lib/replaceLib');
const getExternalResources = require('./getExternalResources');

const isDev = process.env.NODE_ENV === 'development';
const usePreact = process.env.REACT_ENV === 'preact';

function alertBabelConfig(rules) {
  rules.forEach((rule) => {
    if (rule.loader && rule.loader === 'babel-loader') {
      if (rule.options.plugins.indexOf(replaceLib) === -1) {
        rule.options.plugins.push(replaceLib);
      }
      rule.options.plugins = rule.options.plugins.filter(plugin =>
        !plugin.indexOf || plugin.indexOf('babel-plugin-add-module-exports') === -1
      );
    } else if (rule.use) {
      alertBabelConfig(rule.use);
    }
  });
}

module.exports = {
  port: 8001,
  source: {
    components: './components',
    docs: './docs',
    changelog: [
      'CHANGELOG.zh-CN.md',
      'CHANGELOG.en-US.md',
    ],
  },
  theme: './site/theme',
  htmlTemplate: './site/theme/static/template.html',
  themeConfig: {
    categoryOrder: {
      'Ant Design': 0,
      原则: 1,
      Principles: 1,
      视觉: 2,
      Visual: 2,
      模式: 3,
      Patterns: 3,
      其他: 6,
      Other: 6,
      Components: 100,
    },
    typeOrder: {
      General: 0,
      Layout: 1,
      Navigation: 2,
      'Data Entry': 3,
      'Data Display': 4,
      Feedback: 5,
      Localization: 6,
      Other: 7,
    },
    docVersions: {
      '0.9.x': 'http://09x.ant.design',
      '0.10.x': 'http://010x.ant.design',
      '0.11.x': 'http://011x.ant.design',
      '0.12.x': 'http://012x.ant.design',
      '1.x': 'http://1x.ant.design',
      '2.x': 'http://2x.ant.design',
    },
  },
  filePathMapper(filePath) {
    if (filePath === '/index.html') {
      return ['/index.html', '/index-cn.html'];
    }
    if (filePath.endsWith('/index.html')) {
      return [filePath, filePath.replace(/\/index\.html$/, '-cn/index.html')];
    }
    if (filePath !== '/404.html' && filePath !== '/index-cn.html') {
      return [filePath, filePath.replace(/\.html$/, '-cn.html')];
    }
    return filePath;
  },
  doraConfig: {
    verbose: true,
    plugins: ['dora-plugin-upload'],
  },
  webpackConfig(config) {
    config.resolve.alias = {
      'antd/lib': path.join(process.cwd(), 'components'),
      'antd/es': path.join(process.cwd(), 'components'),
      antd: path.join(process.cwd(), 'index'),
      site: path.join(process.cwd(), 'site'),
      'react-router': 'react-router/umd/ReactRouter',
    };

    config.externals = {
      'react-router-dom': 'ReactRouterDOM',
    };

    if (usePreact) {
      config.resolve.alias = Object.assign({}, config.resolve.alias, {
        react: 'preact-compat',
        'react-dom': 'preact-compat',
        'create-react-class': 'preact-compat/lib/create-react-class',
        'react-router': 'react-router',
      });
    }

    if (isDev) {
      config.devtool = 'source-map';
    }

    alertBabelConfig(config.module.rules);

    config.plugins.push(
      new CSSSplitWebpackPlugin({ size: 4000 }),
      new OfflinePlugin({
        appShell: '/app-shell',
        caches: {
          main: [':rest:'],
          additional: [':externals:'],
        },
        externals: [
          '/app-shell',
          'https://at.alicdn.com/t/font_148784_v4ggb6wrjmkotj4i.woff',
          'https://at.alicdn.com/t/font_148784_v4ggb6wrjmkotj4i.eot',
          'https://at.alicdn.com/t/font_148784_v4ggb6wrjmkotj4i.svg',
          'https://at.alicdn.com/t/font_148784_v4ggb6wrjmkotj4i.ttf',
        ].concat(getExternalResources()),
        responseStrategy: 'network-first',
        safeToUseOptionalCaches: true,
        ServiceWorker: {
          events: true,
        },
        AppCache: false,
      }),
    );

    return config;
  },

  htmlTemplateExtraData: {
    isDev,
    usePreact,
  },
};
<|endoftext|>


<commit_before>var sys = require('sys');
var couchdb = require('node-couchdb/lib/couchdb');
var couchClient;
var trackedDatabases = {};

if (require.main == module) {
  // NB: we're executing, not being require()'ed
  start();
  process.addListener('uncaughtException', function (err) {
    log('ERROR', err);
  });
}

function start() {
  log('INFO', 'Starting changes loader');
  couchClient = getClientFromArgs(process.argv);
  log('DEBUG', couchClient.host, couchClient.port);
  couchClient.allDbs(checkForNewDBs);
}

function checkForNewDBs(err, dbNames) {
  log('DEBUG', 'Received DB names', dbNames);
  if (err) {
    throw err;
  } else {
    dbNames.forEach(function(dbName) {
      if (!trackedDatabases[dbName]) {
        log('DEBUG', 'Found new database', dbName);
        var db = couchClient.db(dbName);
        trackedDatabases[dbName] = db;
        setupDBListener(db);
        db.allDocs({
          startkey: "_design/",
          endkey: "_design0",
          include_docs: true
        }, function(err, resp) {
          if (err) throw err;
          resp.rows.forEach(function(row) {
            createClientHandlers(db, row.doc);
          });
        });
      } else {
        log('DEBUG', 'Database already tracked', dbName);
      }
    });
    setTimeout(function() {
      log('DEBUG', 'Checking for new datbases');
      couchClient.allDbs(checkForNewDBs);
    }, 60 * 1000);
  }
}

function setupDBListener(db) {
  db.clientHandlers = {};
  db.info(function(err, info) {
    var opts = { since: info.update_seq };
    log('DEBUG', 'Listening to changes on', db.name);
    db.changesStream(opts).addListener('data', function(change) {
      var docID = change.id;
      if (/^_design\//.test(docID)) {
        log('INFO', 'Design doc changed', docID);
        db.getDoc(docID, function(err, ddoc) {
          if (err) throw err;
          createClientHandlers(db, ddoc);
        });
      } else {
        Object.keys(db.clientHandlers).forEach(function(ddocName) {
          db.clientHandlers[ddocName].forEach(function(handlerFun) {
            handlerFun(change);
          });
        });
      }
    });
  });
}

function createClientHandlers(db, ddoc) {
  var handlers = [];
  if (ddoc.changes) {
    Object.keys(ddoc.changes).forEach(function(handlerName) {
      var fullName = [db.name, ddoc._id, handlerName].join('/');
      log('DEBUG', 'Setting up changes handler for ' + fullName);
      handlers.push(compileHandler(handlerName, ddoc, db.name));
    });
  }
  db.clientHandlers[ddoc._id] = handlers;
}

function compileHandler(name, ddoc, dbName) {
  var fullName = [dbName, ddoc._id, name].join('/');
  var clientLogFun = function(msg) { log('CLIENT', fullName, msg); };
  var code = ddoc.changes[name];
  var context = { ddoc: ddoc, require: require, log: clientLogFun };
  var fun = process.evalcx(
    '(' + code  + ');',
    context,
    fullName
  );
  if (typeof fun !== 'function') {
    throw fullName + ' does not evaluate to a function';
  }
  return function(change) {
    log('DEBUG', "Handler called - " + fullName);
    try {
      fun(change);
    } catch (err) {
      log('ERROR', fullName, err);
    }
  };
}

function log() {
  var args = Array.prototype.slice.apply(arguments);
  var level = args.shift();
  sys.puts(level + ": " + sys.inspect(args, false, 10));
}

function getClientFromArgs(args) {
  var databaseURL = args[args.length - 1];
  var urlParts = require('url').parse(databaseURL);
  return couchdb.createClient(urlParts.port, urlParts.hostname);
}
<commit_msg>needless refactor;<commit_after>var sys = require('sys');
var couchdb = require('node-couchdb/lib/couchdb');
var couchClient;
var trackedDatabases = {};

if (require.main == module) {
  // NB: we're executing, not being require()'ed
  start();
  process.addListener('uncaughtException', function (err) {
    log('ERROR', err);
  });
}

function start() {
  log('INFO', 'Starting changes loader');
  couchClient = getClientFromArgs(process.argv);
  log('DEBUG', couchClient.host, couchClient.port);
  couchClient.allDbs(checkForNewDBs);
}

function checkForNewDBs(err, dbNames) {
  log('DEBUG', 'Received DB names', dbNames);
  if (err) {
    throw err;
  } else {
    dbNames.forEach(function(dbName) {
      if (!trackedDatabases[dbName]) {
        trackNewDB(dbName);
      } else {
        log('DEBUG', 'Database already tracked', dbName);
      }
    });
    setTimeout(function() {
      log('DEBUG', 'Checking for new datbases');
      couchClient.allDbs(checkForNewDBs);
    }, 60 * 1000);
  }
}

function trackNewDB(dbName) {
  log('DEBUG', 'Found new database', dbName);
  var db = couchClient.db(dbName);
  trackedDatabases[dbName] = db;
  setupDBListener(db);
  db.allDocs({
    startkey: "_design/",
    endkey: "_design0",
    include_docs: true
  }, function(err, resp) {
    if (err) throw err;
    resp.rows.forEach(function(row) {
      createClientHandlers(db, row.doc);
    });
  });
}

function setupDBListener(db) {
  db.clientHandlers = {};
  db.info(function(err, info) {
    var opts = { since: info.update_seq };
    log('DEBUG', 'Listening to changes on', db.name);
    db.changesStream(opts).addListener('data', function(change) {
      var docID = change.id;
      if (/^_design\//.test(docID)) {
        log('INFO', 'Design doc changed', docID);
        db.getDoc(docID, function(err, ddoc) {
          if (err) throw err;
          createClientHandlers(db, ddoc);
        });
      } else {
        Object.keys(db.clientHandlers).forEach(function(ddocName) {
          db.clientHandlers[ddocName].forEach(function(handlerFun) {
            handlerFun(change);
          });
        });
      }
    });
  });
}

function createClientHandlers(db, ddoc) {
  var handlers = [];
  if (ddoc.changes) {
    Object.keys(ddoc.changes).forEach(function(handlerName) {
      var fullName = [db.name, ddoc._id, handlerName].join('/');
      log('DEBUG', 'Setting up changes handler for ' + fullName);
      handlers.push(compileHandler(handlerName, ddoc, db.name));
    });
  }
  db.clientHandlers[ddoc._id] = handlers;
}

function compileHandler(name, ddoc, dbName) {
  var fullName = [dbName, ddoc._id, name].join('/');
  var clientLogFun = function(msg) { log('CLIENT', fullName, msg); };
  var code = ddoc.changes[name];
  var context = { ddoc: ddoc, require: require, log: clientLogFun };
  var fun = process.evalcx(
    '(' + code  + ');',
    context,
    fullName
  );
  if (typeof fun !== 'function') {
    throw fullName + ' does not evaluate to a function';
  }
  return function(change) {
    log('DEBUG', "Handler called - " + fullName);
    try {
      fun(change);
    } catch (err) {
      log('ERROR', fullName, err);
    }
  };
}

function log() {
  var args = Array.prototype.slice.apply(arguments);
  var level = args.shift();
  sys.puts(level + ": " + sys.inspect(args, false, 10));
}

function getClientFromArgs(args) {
  var databaseURL = args[args.length - 1];
  var urlParts = require('url').parse(databaseURL);
  return couchdb.createClient(urlParts.port, urlParts.hostname);
}
<|endoftext|>


<commit_before>// ==UserScript==
// @name         autoscroll
// @namespace    autoscroll
// @version      1.13
// @description  autoscroller to be used with https://facebook.tracking.exposed, This userscript works with TamperMoneky extension.
// @author       Claudio Agosti @_vecna
// @match        https://www.facebook.com/*
// @connect      autoscroll
// @grant        GM_setValue
// @grant        GM_getValue
// @require      https://cdnjs.cloudflare.com/ajax/libs/lodash-compat/3.10.2/lodash.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.13.0/moment.min.js
// ==/UserScript==

var times = 30;
var delay = 5;
var fixedH = 800;
var plan = [
    "08:01",
    "10:01",
    "12:01",
    "12:21",
    "12:41",
    "13:01",
    "13:21",
    "13:41",
    "14:01",
    "15:01",
    "16:01",
    "17:01",
    "18:01",
    "19:01",
    "20:01",
    "21:01",
];

function timeline(reference) {

    if(!reference) {
        var s = GM_getValue("scrolling");

        if(s && moment(s).add(50, 's').isBefore(moment())) {
            console.log("a previous scroll interrupted?");
        }

        if(s && moment(s).add(50, 's').isAfter(moment())) {
            return;
        }

        console.log("setting GM_setValue 'scrolling'", moment().format() );
        GM_setValue("scrolling", moment().format());

        reference = {
            counter: 0,
            y: 0,
        };
    }

    if(reference.counter === times) {
        var s = GM_getValue("scrolling");
        console.log("Timeline counter reach", times);
        if(s) {
            console.log(s, "'scrolling': is present, -> doTheNext, removing GM_[scrolling]", s);
            GM_setValue("scrolling", null);
            return _.delay(doTheNext, 1);
        } else {
            console.log("GM_[scrolling] is null", s, "killed ramification");
        }
    } else {
        reference.counter += 1;
        reference.y = reference.counter * fixedH;
        GM_setValue("scrolling", moment().format());

        console.log("scrolling", reference.counter, "at",
                  moment().format("HH:mm:ss"), "a the Y", reference.y);

        scrollTo(0, reference.y);

        return _.delay(timeline, delay * 1000, reference);
    }
}

function doTheNext() {

    var isNight = moment().hour();
    var subdays = 0;
    if(isNight < 4) {
        console.log("It is night -- (FIXME: this check is not timezone safe)");
        subdays = 1;
    }
    /*
     * This function has to compute the seconds of distance between
     * now and the next TESING-HOUR. the hours are 9:00,11:00 etc,
     * and they happen in GMT-3, (180 minutes, below in the variables)
     */

	var tinfo = _.map(plan, function(t) {

        var GMTARG = 180;
		var hour = _.parseInt(t.split(':')[0]);
		var minute = _.parseInt(t.split(':')[1]);
		var personalO = moment().utcOffset() + GMTARG;
		var target = moment().set({
            hour: hour,
            minute: minute,
            second: 0
        }).utcOffset(GMTARG);
        target.subtract(subdays, 'd');
        var secsdiff = moment.duration(target - moment()).asSeconds();
		var secto =  secsdiff + (personalO * 60);

		return {
            secsdiff: secsdiff,
			hto: _.round(secto / 3600, 1),
			secto: secto,
			target: t,
			x: target.format()
		};
	});

	console.log(tinfo);
	var next = _.first(_.filter(tinfo, function(t) {
		return t.secto > 0;
	}));

    console.log("Schedule computed, next on", next);
    if(!next) {
        console.log("Night problem, check back in 1 hour");
        GM_setValue("refresh", true);
        return _.delay(doTheNext, 3600 * 1000);
    } else {
        console.log("Setting the next timeline to", next.secto);
        GM_setValue("refresh", true);
        return _.delay(cleanAndReload, next.secto * 1000);
    }
};

function cleanAndReload() {
    GM_setValue("scrolling", null);
    // this value 'refresh' is not used because remain dirty in case a browser restart
    GM_setValue("refresh", null);
    location.reload();
};

(function() {

    var s = GM_getValue("scrolling");

    if( s && moment(s).add(50, 's').isBefore(moment())) {
        console.log("Considering the diff of",
                moment.duration(moment() - moment(s)).humanize(), "...");
        timeline();
    }
    else if(!s) {
        var r = GM_getValue("refresh");
        console.log("beginning tampermonkey, scrolling", s, "refresh", r);
        timeline();
    } else
        console.log("Nope, recorded is", moment(s).format("HH:mm:ss"), "now", moment().format("HH:mm:ss"));

})();

<commit_msg>three time per day access<commit_after>// ==UserScript==
// @name         autoscroll
// @namespace    autoscroll
// @version      1.14
// @description  autoscroller to be used with https://facebook.tracking.exposed, This userscript works with TamperMoneky extension.
// @author       Claudio Agosti @_vecna
// @match        https://www.facebook.com/*
// @connect      autoscroll
// @grant        GM_setValue
// @grant        GM_getValue
// @require      https://cdnjs.cloudflare.com/ajax/libs/lodash-compat/3.10.2/lodash.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.13.0/moment.min.js
// ==/UserScript==

var times = 30;
var delay = 5;
var fixedH = 800;
var plan = [
    "10:01",
    "14:01",
    "18:01"
];

function timeline(reference) {

    if(!reference) {
        var s = GM_getValue("scrolling");

        if(s && moment(s).add(50, 's').isBefore(moment())) {
            console.log("a previous scroll interrupted?");
        }

        if(s && moment(s).add(50, 's').isAfter(moment())) {
            return;
        }

        console.log("setting GM_setValue 'scrolling'", moment().format() );
        GM_setValue("scrolling", moment().format());

        reference = {
            counter: 0,
            y: 0,
        };
    }

    if(reference.counter === times) {
        var s = GM_getValue("scrolling");
        console.log("Timeline counter reach", times);
        if(s) {
            console.log(s, "'scrolling': is present, -> doTheNext, removing GM_[scrolling]", s);
            GM_setValue("scrolling", null);
            return _.delay(doTheNext, 1);
        } else {
            console.log("GM_[scrolling] is null", s, "killed ramification");
        }
    } else {
        reference.counter += 1;
        reference.y = reference.counter * fixedH;
        GM_setValue("scrolling", moment().format());

        console.log("scrolling", reference.counter, "at",
                  moment().format("HH:mm:ss"), "a the Y", reference.y);

        scrollTo(0, reference.y);

        return _.delay(timeline, delay * 1000, reference);
    }
}

function doTheNext() {

    var isNight = moment().hour();
    var subdays = 0;
    if(isNight < 4) {
        console.log("It is night -- (FIXME: this check is not timezone safe)");
        subdays = 1;
    }
    /*
     * This function has to compute the seconds of distance between
     * now and the next TESING-HOUR. the hours are 9:00,11:00 etc,
     * and they happen in GMT-3, (180 minutes, below in the variables)
     */

	var tinfo = _.map(plan, function(t) {

        var GMTARG = 180;
		var hour = _.parseInt(t.split(':')[0]);
		var minute = _.parseInt(t.split(':')[1]);
		var personalO = moment().utcOffset() + GMTARG;
		var target = moment().set({
            hour: hour,
            minute: minute,
            second: 0
        }).utcOffset(GMTARG);
        target.subtract(subdays, 'd');
        var secsdiff = moment.duration(target - moment()).asSeconds();
		var secto =  secsdiff + (personalO * 60);

		return {
            secsdiff: secsdiff,
			hto: _.round(secto / 3600, 1),
			secto: secto,
			target: t,
			x: target.format()
		};
	});

	console.log(tinfo);
	var next = _.first(_.filter(tinfo, function(t) {
		return t.secto > 0;
	}));

    console.log("Schedule computed, next on", next);
    if(!next) {
        console.log("Night problem, check back in 1 hour");
        GM_setValue("refresh", true);
        return _.delay(doTheNext, 3600 * 1000);
    } else {
        console.log("Setting the next timeline to", next.secto);
        GM_setValue("refresh", true);
        return _.delay(cleanAndReload, next.secto * 1000);
    }
};

function cleanAndReload() {
    GM_setValue("scrolling", null);
    // this value 'refresh' is not used because remain dirty in case a browser restart
    GM_setValue("refresh", null);
    location.reload();
};

(function() {

    var s = GM_getValue("scrolling");

    if( s && moment(s).add(50, 's').isBefore(moment())) {
        console.log("Considering the diff of",
                moment.duration(moment() - moment(s)).humanize(), "...");
        timeline();
    }
    else if(!s) {
        var r = GM_getValue("refresh");
        console.log("beginning tampermonkey, scrolling", s, "refresh", r);
        timeline();
    } else
        console.log("Nope, recorded is", moment(s).format("HH:mm:ss"), "now", moment().format("HH:mm:ss"));

})();

<|endoftext|>


<commit_before>( function(){

  /**
   * Mappers are used to store a scope of a DOM tree
   * to map and a registry of mappers to execute
   * @class Mapper
   * @constructor
   */
  Lu.Mapper = function(){
    var scope = window.LU_CONFIG.scope || document;
    this.$scope = undefined;
    this.maps = [];
    this.setScope( scope );
  };

  /**
   * Sets the scope to use when mapping components to nodes
   * @public
   * @method setScoped
   * @param {*} scope This can be an HTML Element,
   * an array of elements, a selector string or a jquery object
   * @return this
   */
  Lu.Mapper.prototype.setScope = function( scope ){
    var $scope;

    function filter( item, index ){
      var lu = item.getAttribute( 'data-lu' );
      return ( lu !== null && lu !== '' );
    }

    if( scope instanceof $ ){
      $scope = scope.find( '[data-lu]' ).add( _.filter( scope, filter ) );
    } else if( scope.getElementsByTagName ){
      $scope = $( _.filter( scope.getElementsByTagName( '*' ), filter ) );
    } else {
      $scope = $( scope );
      $scope = $scope.find( '[data-lu]' ).add( _.filter( $scope, filter ) );
    }

    this.$scope = $scope;
    return this;
  };

  /**
   * Gets a jquery object containing all Lu decorated nodes
   * @public
   * @method setScoped
   * @return a jquery object
   */
  Lu.Mapper.prototype.getScope = function(){
    return this.$scope;
  };

  /**
   * Registers a mapper to be executed later
   * @public
   * @method register
   * @param {Function} map A function mapper to be called
   * @return this
   */
  Lu.Mapper.prototype.register = function( map ){
    this.maps.unshift( map );
    return this;
  };

  /**
   * Executes all registered maps
   * @public
   * @method execute
   * @return this
   */
  Lu.Mapper.prototype.execute = function(){
    _.each( this.maps, function( item, index ){
      item.call();
    } );
  };

  //Create a new Mapper to contain default mappings for Lu.
  var Mapper = new Lu.Mapper();

  //Generic Mappers
  _.each( ['Switch', 'List', 'Carousel', 'Container', 'Tip'], function( id, index ){
    Mapper.register( function(){
      Lu.map( _.filter( Mapper.$scope, function( item, index ){
        return ( item.getAttribute( 'data-lu' ).indexOf( id ) > -1 );
      } ), id );
    } );
  } );

  // Coalesce the buttons into one mapper function, because we care about performance,
  // saving bytes, and abhor redundancy.
  _.each( ['select', 'first', 'last', 'next', 'previous', 'load', 'play', 'pause', 'state'], function( action ) {

    var scope = _.filter( Mapper.$scope, function( item ){
      var nodeName = item.nodeName;
      return ( nodeName === 'BUTTON' || nodeName === 'A' || nodeName === 'INPUT' );
    } );

    Mapper.register( function(){
      var key = 'Button:' + action.charAt( 0 ).toUpperCase() + action.substring( 1 );
      Lu.map( _.filter( scope, function( item ){
        return ( item.getAttribute( 'data-lu' ).indexOf( key ) > -1 );
      } ), 'Button', function(){
        this.settings.action = action;
        this.key = key;
        this.hasDependencies = true;
      } );
    } );
  } );

  // Tip (tooltip)
  var tips = _.filter( Mapper.$scope, function( item ){
    return ( item.getAttribute( 'data-lu' ).indexOf( 'Tip' ) > -1 );
  } );

  Mapper.register( function(){
    var key = 'Tip';
    Lu.map( _.filter( tips, function( item ){
      return ( _.indexOf( item.getAttribute( 'data-lu' ).split( ' ' ), key ) > -1 );
    } ), 'Tip', function(){
      this.settings.placement = 'Right';
      this.key = key;
      this.hasDependencies = true;
    } );
  } );

  _.each( ['Above', 'Below', 'Left', 'Right'], function( placement ) {

    Mapper.register( function(){
      var key = 'Tip:' + placement;
      Lu.map( _.filter( tips, function( item ){
        return ( item.getAttribute( 'data-lu' ).indexOf( key ) > -1 );
      } ), 'Tip', function(){
        this.settings.placement = placement;
        this.key = key;
        this.hasDependencies = true;
      } );
    } );
  } );

  //Placeholder
  Mapper.register( function(){
    Lu.map( _.filter( Mapper.$scope, function( item, index ){
      return ( item.getAttribute( 'data-lu' ).indexOf( 'Placeholder' ) > -1 &&
             ( item.nodeName === 'INPUT' || item.nodeName === 'TEXTAREA' ) &&
             item.getAttribute( 'placeholder' ) );
    } ), 'Placeholder' );
  } );

  //Execute Default Mappers
  Mapper.execute();

}() );
<commit_msg>Forgot to remove Tip from other mapper.<commit_after>( function(){

  /**
   * Mappers are used to store a scope of a DOM tree
   * to map and a registry of mappers to execute
   * @class Mapper
   * @constructor
   */
  Lu.Mapper = function(){
    var scope = window.LU_CONFIG.scope || document;
    this.$scope = undefined;
    this.maps = [];
    this.setScope( scope );
  };

  /**
   * Sets the scope to use when mapping components to nodes
   * @public
   * @method setScoped
   * @param {*} scope This can be an HTML Element,
   * an array of elements, a selector string or a jquery object
   * @return this
   */
  Lu.Mapper.prototype.setScope = function( scope ){
    var $scope;

    function filter( item, index ){
      var lu = item.getAttribute( 'data-lu' );
      return ( lu !== null && lu !== '' );
    }

    if( scope instanceof $ ){
      $scope = scope.find( '[data-lu]' ).add( _.filter( scope, filter ) );
    } else if( scope.getElementsByTagName ){
      $scope = $( _.filter( scope.getElementsByTagName( '*' ), filter ) );
    } else {
      $scope = $( scope );
      $scope = $scope.find( '[data-lu]' ).add( _.filter( $scope, filter ) );
    }

    this.$scope = $scope;
    return this;
  };

  /**
   * Gets a jquery object containing all Lu decorated nodes
   * @public
   * @method setScoped
   * @return a jquery object
   */
  Lu.Mapper.prototype.getScope = function(){
    return this.$scope;
  };

  /**
   * Registers a mapper to be executed later
   * @public
   * @method register
   * @param {Function} map A function mapper to be called
   * @return this
   */
  Lu.Mapper.prototype.register = function( map ){
    this.maps.unshift( map );
    return this;
  };

  /**
   * Executes all registered maps
   * @public
   * @method execute
   * @return this
   */
  Lu.Mapper.prototype.execute = function(){
    _.each( this.maps, function( item, index ){
      item.call();
    } );
  };

  //Create a new Mapper to contain default mappings for Lu.
  var Mapper = new Lu.Mapper();

  //Generic Mappers
  _.each( ['Switch', 'List', 'Carousel', 'Container'], function( id, index ){
    Mapper.register( function(){
      Lu.map( _.filter( Mapper.$scope, function( item, index ){
        return ( item.getAttribute( 'data-lu' ).indexOf( id ) > -1 );
      } ), id );
    } );
  } );

  // Coalesce the buttons into one mapper function, because we care about performance,
  // saving bytes, and abhor redundancy.
  _.each( ['select', 'first', 'last', 'next', 'previous', 'load', 'play', 'pause', 'state'], function( action ) {

    var scope = _.filter( Mapper.$scope, function( item ){
      var nodeName = item.nodeName;
      return ( nodeName === 'BUTTON' || nodeName === 'A' || nodeName === 'INPUT' );
    } );

    Mapper.register( function(){
      var key = 'Button:' + action.charAt( 0 ).toUpperCase() + action.substring( 1 );
      Lu.map( _.filter( scope, function( item ){
        return ( item.getAttribute( 'data-lu' ).indexOf( key ) > -1 );
      } ), 'Button', function(){
        this.settings.action = action;
        this.key = key;
        this.hasDependencies = true;
      } );
    } );
  } );

  // Tip (tooltip)
  var tips = _.filter( Mapper.$scope, function( item ){
    return ( item.getAttribute( 'data-lu' ).indexOf( 'Tip' ) > -1 );
  } );

  Mapper.register( function(){
    var key = 'Tip';
    Lu.map( _.filter( tips, function( item ){
      return ( _.indexOf( item.getAttribute( 'data-lu' ).split( ' ' ), key ) > -1 );
    } ), 'Tip', function(){
      this.settings.placement = 'Right';
      this.key = key;
      this.hasDependencies = true;
    } );
  } );

  _.each( ['Above', 'Below', 'Left', 'Right'], function( placement ) {

    Mapper.register( function(){
      var key = 'Tip:' + placement;
      Lu.map( _.filter( tips, function( item ){
        return ( item.getAttribute( 'data-lu' ).indexOf( key ) > -1 );
      } ), 'Tip', function(){
        this.settings.placement = placement;
        this.key = key;
        this.hasDependencies = true;
      } );
    } );
  } );

  //Placeholder
  Mapper.register( function(){
    Lu.map( _.filter( Mapper.$scope, function( item, index ){
      return ( item.getAttribute( 'data-lu' ).indexOf( 'Placeholder' ) > -1 &&
             ( item.nodeName === 'INPUT' || item.nodeName === 'TEXTAREA' ) &&
             item.getAttribute( 'placeholder' ) );
    } ), 'Placeholder' );
  } );

  //Execute Default Mappers
  Mapper.execute();

}() );
<|endoftext|>