<commit_before>"use strict";

var
    esPhinx;

(function ($) {

    $.extend({

        prototype: {

            //
|

            // text: function () {
            // },

            visible: function () {
                var
                    self = this,
                    first = self.first(),
                    node;

                if (first) {
                    node = $(first);
                    if (node.css("display") === "none"
                        || node.css("visibility") === "hidden"
                        || node.css("opacity") === 0) {
                        return false;
                    }

                    return true;
                }
            },

            tagName: function () {
                var
                    self = this;

                if (self.count()) {
                    return self.property("tagName");
                }

                return "";
            },

            hasClass: function (classList) {
                var
                    self = this,
                    first = $(self.first()),
                    pattern;

                if (arguments.length > 1) {
                    classList = Array.from(arguments);
                    pattern = new RegExp("(?=.*(\\b" + classList
                        .join("(?=(\\b| ))))(?=.*(\\b")
                        + "(?=(\\b| )))).*", "g");
                } else {
                    pattern = new RegExp("(?=.*(\\b" + classList.split(" ")
                        .join("(?=(\\b| ))))(?=.*(\\b")
                        + "(?=(\\b| )))).*", "g");
                }

                if (pattern.test(first.attr("class"))) {
                    return true;
                }

                return false;
            },

            remove: function (node) {
                var
                    self = this;

                if (node) {
                    $(node).remove();
                } else {
                    self.each(function (node) {
                        // self.parent().first().removeChild(node);
                        self.parent().removeChild(node);
                    });
                }

                return self;
            },

            removeClass: function (classList) {
                var
                    self = this,
                    currentClassList,
                    pattern,
                    removed;

                if (arguments.length > 1) {
                    classList = Array.from(arguments);
                    pattern = new RegExp("\\b((" + classList
                        .join(")|(")  + "))(?=(\\b| ))", "g");
                } else {
                    pattern = new RegExp("\\b((" + classList.split(" ")
                        .join(")|(") + "))(?=(\\b| ))", "g");
                }

                self.each(function (node) {
                    currentClassList = $(node).attr("class");
                    removed = currentClassList.replace(pattern, "");
                    $(node).attr("class", removed.replace(/(  )+/g, " ").trim());
                });

                return self;
            },

            addClass: function (classList) {
                var
                    self = this,
                    currentClassList,
                    asString;

                if (arguments.length > 1) {
                    classList = Array.from(arguments);
                    asString = classList.join(" ");
                } else if (typeof classList === "string") {
                    asString = classList;
                }

                self.each(function (node) {
                    currentClassList = $(node).attr("class") || "";
                    $(node).attr("class", (currentClassList + " " + asString)
                        .trim());
                });

                return self;
            },

            check: function () {
                var
                    self = this;

                if (!self.checked()) {
                    self.property("checked", true);
                }

                return self;
            },

            uncheck: function () {
                var
                    self = this;

                return self.property("checked", false);
            },

            removeAttr: function (attrName) {
                var
                    self = this;

                self.each(function (node) {
                    node.removeAttribute(attrName);
                });

                return self;
            },

            attr: function (attrName, value) {
                var
                    self = this,
                    first = self.first(),
                    attr;

                if (self.length === 0) {
                    return self;
                }

                if (typeof value === "boolean" || value || value === "") {
                    self.each(function (node) {
                        node.setAttribute(attrName, value);
                    });

                    return self;
                } else {
                    if (attr = first.getAttribute(attrName)) {
                        return attr;
                    }
                }

                return undefined;
            },

            property: function (propertyName, value) {
                var
                    self = this,
                    first = self.first();

                if (self.length === 0) {
                    return self;
                }

                if (typeof value === "boolean" || value || value === "") {
                    self.each(function (node) {
                        node[propertyName] = value;
                    });

                    return self;
                } else {
                    if (first[propertyName] ||
                        typeof first[propertyName] === "boolean") {
                        return first[propertyName];
                    }
                }

                return undefined;
            },

            deflate: function () {
                var
                    self = this;

                self.each(function (node) {
                    node.innerHTML = null;
                });

                return self;
            },

            insertAfter: function(element) {
                var
                    self = this,
                    collection = $(element);

                self.each(function (node) {
                    collection.each(function (element) {
                        element.parentNode.insertBefore(node, element.nextSibling);
                    });
                });

                return self;
            },

            insertBefore: function(node) {
                var
                    self = this,
                    collection = $(node);

                self.each(function (element) {
                    collection.each(function (node) {
                        node.parentNode.insertBefore(element, node);
                    });
                });

                return self;
            },

            prepend: function(node) {
                var
                    self = this,
                    firstNode;

                self.each(function (element) {
                    firstNode = $(element).firstNode();
                    if (firstNode.count()) {
                        if (typeof node === "string") {
                            $(document.createTextNode(node))
                                .insertBefore(firstNode);
                        } else {
                            $(node).insertBefore(firstNode);
                        }
                    } else {
                        // if (typeof node === "string") {
                        //     $(element).append(document.createTextNode(node));
                        // } else {
                        //     $(element).append(node);
                        // }

                        $(element).append(node);
                    }
                });

                return self;
            },

            text: function (text) {
                var
                    self = this,
                    selectorsPattern = /(^[*.#])|(\[.+\])|(:.+)|(.+[+,~>].+)/g,
                    first;

                if (text) {
                    if (self.count()) {
                        self.each(function (node) {
                            first = esPhinx(node).childNodes().first()
                            if (first instanceof Text) {
                                first.textContent = text;
                            } else {
                                $(node).prepend(text);
                            }
                        });
                    } else {
                        self.push(document.createTextNode(text));
                    }
                } else {
                    if (self.childNodes().count()) {
                        return self.childNodes().first().textContent.trim();
                    } else {
                        return "";
                    }
                }

                return self;
            },

            append: function(node) {
                var
                    self = this,
                    collection;

                if (typeof node === "string") {
                    collection = $().text(node);
                } else {
                    collection = $(node);
                }

                self.each(function (element) {
                    collection.each(function (node) {
                        element.appendChild(node);
                    });
                });

                return self;
            },

            html: function (content) {
                var
                    self = this,
                    collection;

                self.deflate();
                self.append(content);

                return self;
            }

        }

    });

}(esPhinx));

/*$.prototype.ancestors = function (nodeName) {
    var _parents = []
        ,parent = null
    ;
    collection.forEach(function (e) {
        parent = e.parentNode;
        if (nodeName) {
            if (parent.nodeName.toLowerCase() === nodeName) {
                _parents.push(parent);
            } else {
                while(true) {
                    parent = parent.parentNode;
                    if(parent){
                        if(parent.nodeName.toLowerCase() === nodeName) {
                            _parents.push(parent);
                            break;
                        }
                    } else {
                        break;
                    }
                }
            }
        } else {
            if(parent){
                _parents.push(parent);
            }
        }
    });
    return _parents;
}*/
<commit_msg>add method is<commit_after>"use strict";

var
    esPhinx;

(function ($) {

    $.extend({

        prototype: {

            //
|

            // text: function () {
            // },

            is: function (comparedNode) {
                var
                    self = this,
                    first = self.first();

                if (comparedNode.constructor === $) {
                    comparedNode = comparedNode.first();
                }

                if (first) {
                    if (first.isEqualNode(comparedNode)) {
                        return true;
                    }
                }

                return false;
            },

            visible: function () {
                var
                    self = this,
                    first = self.first(),
                    node;

                if (first) {
                    node = $(first);
                    if (node.css("display") === "none"
                        || node.css("visibility") === "hidden"
                        || node.css("opacity") === 0) {
                        return false;
                    }

                    return true;
                }
            },

            tagName: function () {
                var
                    self = this;

                if (self.count()) {
                    return self.property("tagName");
                }

                return "";
            },

            hasClass: function (classList) {
                var
                    self = this,
                    first = $(self.first()),
                    pattern;

                if (arguments.length > 1) {
                    classList = Array.from(arguments);
                    pattern = new RegExp("(?=.*(\\b" + classList
                        .join("(?=(\\b| ))))(?=.*(\\b")
                        + "(?=(\\b| )))).*", "g");
                } else {
                    pattern = new RegExp("(?=.*(\\b" + classList.split(" ")
                        .join("(?=(\\b| ))))(?=.*(\\b")
                        + "(?=(\\b| )))).*", "g");
                }

                if (pattern.test(first.attr("class"))) {
                    return true;
                }

                return false;
            },

            remove: function (node) {
                var
                    self = this;

                if (node) {
                    $(node).remove();
                } else {
                    self.each(function (node) {
                        // self.parent().first().removeChild(node);
                        self.parent().removeChild(node);
                    });
                }

                return self;
            },

            removeClass: function (classList) {
                var
                    self = this,
                    currentClassList,
                    pattern,
                    removed;

                if (arguments.length > 1) {
                    classList = Array.from(arguments);
                    pattern = new RegExp("\\b((" + classList
                        .join(")|(")  + "))(?=(\\b| ))", "g");
                } else {
                    pattern = new RegExp("\\b((" + classList.split(" ")
                        .join(")|(") + "))(?=(\\b| ))", "g");
                }

                self.each(function (node) {
                    currentClassList = $(node).attr("class");
                    removed = currentClassList.replace(pattern, "");
                    $(node).attr("class", removed.replace(/(  )+/g, " ").trim());
                });

                return self;
            },

            addClass: function (classList) {
                var
                    self = this,
                    currentClassList,
                    asString;

                if (arguments.length > 1) {
                    classList = Array.from(arguments);
                    asString = classList.join(" ");
                } else if (typeof classList === "string") {
                    asString = classList;
                }

                self.each(function (node) {
                    currentClassList = $(node).attr("class") || "";
                    $(node).attr("class", (currentClassList + " " + asString)
                        .trim());
                });

                return self;
            },

            check: function () {
                var
                    self = this;

                if (!self.checked()) {
                    self.property("checked", true);
                }

                return self;
            },

            uncheck: function () {
                var
                    self = this;

                return self.property("checked", false);
            },

            removeAttr: function (attrName) {
                var
                    self = this;

                self.each(function (node) {
                    node.removeAttribute(attrName);
                });

                return self;
            },

            attr: function (attrName, value) {
                var
                    self = this,
                    first = self.first(),
                    attr;

                if (self.length === 0) {
                    return self;
                }

                if (typeof value === "boolean" || value || value === "") {
                    self.each(function (node) {
                        node.setAttribute(attrName, value);
                    });

                    return self;
                } else {
                    if (attr = first.getAttribute(attrName)) {
                        return attr;
                    }
                }

                return undefined;
            },

            property: function (propertyName, value) {
                var
                    self = this,
                    first = self.first();

                if (self.length === 0) {
                    return self;
                }

                if (typeof value === "boolean" || value || value === "") {
                    self.each(function (node) {
                        node[propertyName] = value;
                    });

                    return self;
                } else {
                    if (first[propertyName] ||
                        typeof first[propertyName] === "boolean") {
                        return first[propertyName];
                    }
                }

                return undefined;
            },

            deflate: function () {
                var
                    self = this;

                self.each(function (node) {
                    node.innerHTML = null;
                });

                return self;
            },

            insertAfter: function(element) {
                var
                    self = this,
                    collection = $(element);

                self.each(function (node) {
                    collection.each(function (element) {
                        element.parentNode.insertBefore(node, element.nextSibling);
                    });
                });

                return self;
            },

            insertBefore: function(node) {
                var
                    self = this,
                    collection = $(node);

                self.each(function (element) {
                    collection.each(function (node) {
                        node.parentNode.insertBefore(element, node);
                    });
                });

                return self;
            },

            prepend: function(node) {
                var
                    self = this,
                    firstNode;

                self.each(function (element) {
                    firstNode = $(element).firstNode();
                    if (firstNode.count()) {
                        if (typeof node === "string") {
                            $(document.createTextNode(node))
                                .insertBefore(firstNode);
                        } else {
                            $(node).insertBefore(firstNode);
                        }
                    } else {
                        // if (typeof node === "string") {
                        //     $(element).append(document.createTextNode(node));
                        // } else {
                        //     $(element).append(node);
                        // }

                        $(element).append(node);
                    }
                });

                return self;
            },

            text: function (text) {
                var
                    self = this,
                    selectorsPattern = /(^[*.#])|(\[.+\])|(:.+)|(.+[+,~>].+)/g,
                    first;

                if (text) {
                    if (self.count()) {
                        self.each(function (node) {
                            first = esPhinx(node).childNodes().first()
                            if (first instanceof Text) {
                                first.textContent = text;
                            } else {
                                $(node).prepend(text);
                            }
                        });
                    } else {
                        self.push(document.createTextNode(text));
                    }
                } else {
                    if (self.childNodes().count()) {
                        return self.childNodes().first().textContent.trim();
                    } else {
                        return "";
                    }
                }

                return self;
            },

            append: function(node) {
                var
                    self = this,
                    collection;

                if (typeof node === "string") {
                    collection = $().text(node);
                } else {
                    collection = $(node);
                }

                self.each(function (element) {
                    collection.each(function (node) {
                        element.appendChild(node);
                    });
                });

                return self;
            },

            html: function (content) {
                var
                    self = this,
                    collection;

                self.deflate();
                self.append(content);

                return self;
            }

        }

    });

}(esPhinx));

/*$.prototype.ancestors = function (nodeName) {
    var _parents = []
        ,parent = null
    ;
    collection.forEach(function (e) {
        parent = e.parentNode;
        if (nodeName) {
            if (parent.nodeName.toLowerCase() === nodeName) {
                _parents.push(parent);
            } else {
                while(true) {
                    parent = parent.parentNode;
                    if(parent){
                        if(parent.nodeName.toLowerCase() === nodeName) {
                            _parents.push(parent);
                            break;
                        }
                    } else {
                        break;
                    }
                }
            }
        } else {
            if(parent){
                _parents.push(parent);
            }
        }
    });
    return _parents;
}*/
<|endoftext|>


<commit_before>var artist = require("./modules/artist"),
	song = require("./modules/songs"),
	utilsRegex = require("./utils/regex"),
	cheerio = require("cheerio"),
	request = require("request"),
	util = require("util"),
	async = require("async"),
	Artist = require("./MongoDB/schema").Artist,
	_ = require("underscore"),
	mongoose = require("mongoose");

// https://github.com/MatthewMueller/cheerio
// https://github.com/mikeal/request
// http://nodejs.org/api/util.html
// http://underscorejs.org/
// https://github.com/caolan/async
// http://mongoosejs.com/docs/guide.html

var geniusQuery = {
    albumURLs: function(artistName, callback) {
        var artistURL = "http://rapgenius.com/artists/";
        request(artistURL + artistName, function (error, response, body) {
            if (error || response.statusCode !== 200) {
                error = "Artist not found";
                callback(error);
            } else {
	              var $ = cheerio.load(body);
                callback(null, $(".album_list li a").map(function() {
                    return this.attr("href");
                }));
            }
        });
    },

    albumData: function(baseAlbumURL, callback) {
		    request(baseAlbumURL, function (error, response, body) {
            if (error || response.statusCode !== 200) {
                callback(error);
            } else {
			          var $ = cheerio.load(body);
                var title = utilsRegex
                    .obtainAlbumTitle($("h1.name a.artist")["0"]["next"]["data"]);

			          // Extract album year from albumTitle if present
				        var year = title.match(/\(\d{4}\)/);
                callback(null, {
                    title: title,
                    year: year === null ? -1 : year[0].replace(/(\(|\))/g,""),
                    songURLs: $(".song_list .song_link").map(function() {
                        return this.attr("href");
                    })
                });
            }
        });
    },

    songData: function(songURL, callback) {
		    request(songURL, function (error, response, body) {
			      if (error !== null || response.statusCode !== 200) throw "Couldn't get song: " + songURL;
			      var $ = cheerio.load(body);
            var lyricsText = $(".lyrics_container .lyrics p").text();
            callback(null, {
			          songTitle: utilsRegex.obtainSongTitle($("h1.song_title a")["0"]["next"]["data"]),
				        trackNumber: $(".album_title_and_track_number").text().trim().split(" ")[1],
				        lyricsText: lyricsText,
				        songlyrics: lyricsText.split("\n")
            });
        });
    }

    // artist: function(artistName, callback) {
    //         url: $("[property='og:url']").attr("content"),
    //         name: $("[property='og:title']").attr("content")
    // }
};

// Process artist page

var getAlbums = function (albumURLs, callback) {
    async.parallel(_.map(albumURLs, function(albumURL) {
		    return buildfn(homeURL + albumURL);
    }), callback);
};

var buildfn = function(baseAlbumURL) {
	  // Closure invoked w/ async.parallel
	  var processAlbum = function (callback) {
		    console.log("processing album", baseAlbumURL);
        geniusQuery.albumData(baseAlbumURL, callback);
	  };
	  return processAlbum;
};

// process song(s)
var addSongs = function (year, albumTitle, $, callback) {
	  var processSongsTasks = [];
    $(".song_list .song_link").each(function() {
		    var songURL = (homeURL + this.attr("href"));
		    var track = new song(albumTitle, songURL, year);
		    var task = buildfn2(songURL, track);
		    processSongsTasks.push(task);
    });
    async.parallel(processSongsTasks, function (errors, results) {
		    console.log("All songs in " + albumTitle + " processed", errors, results);
		    callback(errors, results);
    });
};

var buildfn2 = function (songURL, track) {
	  // Closure invoked w/ async.parallel
	  var processSong = function (callback) {
		    console.log('processing song', songURL);
		    request(songURL, function (error, response, body) {
			      if (!error && response.statusCode == 200) {
			          var $ = cheerio.load(body);
			          var songTitle = utilsRegex.obtainSongTitle($("h1.song_title a")["0"]["next"]["data"]);
				        var trackNumber = $(".album_title_and_track_number").text().trim().split(" ")[1];
				        var lyricsText = $(".lyrics_container .lyrics p").text();
				        var songlyrics = lyricsText.split("\n");
				        track.addSongName(songTitle);
				        track.addTrackNumber(trackNumber);
				        track.addArtist(rapper.name);
				        track.addLyrics(songlyrics);
				        rapper.addSong(track);
				        callback(null);
			      } else {
				        console.log("Error retrieveing song details");
				        callback(error);
			      }
		    });
	  };
	  return processSong;
};

var getSongs = function(songURLs, callback) {
    async.parallel(_.map(songURLs, function(x) {
        return function(parallelCallback) {
            geniusQuery.songData(homeURL + x, parallelCallback);
        };
    }), function (errors, songs) {
		    callback(errors, songs);
    });
};

var getSongURLs = function(albums) {
    return _.reduce(albums, function(a, x) {
        return a.concat(x.data(".song_list .song_link").map(function() {
            return this.attr("href");
        }));
    }, []);
};
// Connect to DB
mongoose.connect("mongodb://localhost/rapgenius");
var homeURL = "http://rapgenius.com";
var rapper = new artist(process.argv[2] || "Skinnyman");
geniusQuery.albumURLs(rapper.name, function(error, albumURLs) {
    getAlbums(albumURLs, function(error, albums) {
        getSongs(getSongURLs(albums), function(error, songs) {
				    process.exit(0);
        });
    });
});
<commit_msg>Replace getSongURLs() with a pluck()<commit_after>var artist = require("./modules/artist"),
	song = require("./modules/songs"),
	utilsRegex = require("./utils/regex"),
	cheerio = require("cheerio"),
	request = require("request"),
	util = require("util"),
	async = require("async"),
	Artist = require("./MongoDB/schema").Artist,
	_ = require("underscore"),
	mongoose = require("mongoose");

// https://github.com/MatthewMueller/cheerio
// https://github.com/mikeal/request
// http://nodejs.org/api/util.html
// http://underscorejs.org/
// https://github.com/caolan/async
// http://mongoosejs.com/docs/guide.html

var geniusQuery = {
    albumURLs: function(artistName, callback) {
        var artistURL = "http://rapgenius.com/artists/";
        request(artistURL + artistName, function (error, response, body) {
            if (error || response.statusCode !== 200) {
                error = "Artist not found";
                callback(error);
            } else {
	              var $ = cheerio.load(body);
                callback(null, $(".album_list li a").map(function() {
                    return this.attr("href");
                }));
            }
        });
    },

    albumData: function(baseAlbumURL, callback) {
		    request(baseAlbumURL, function (error, response, body) {
            if (error || response.statusCode !== 200) {
                callback(error);
            } else {
			          var $ = cheerio.load(body);
                var title = utilsRegex
                    .obtainAlbumTitle($("h1.name a.artist")["0"]["next"]["data"]);

			          // Extract album year from albumTitle if present
				        var year = title.match(/\(\d{4}\)/);
                callback(null, {
                    title: title,
                    year: year === null ? -1 : year[0].replace(/(\(|\))/g,""),
                    songURLs: $(".song_list .song_link").map(function() {
                        return this.attr("href");
                    })
                });
            }
        });
    },

    songData: function(songURL, callback) {
		    request(songURL, function (error, response, body) {
			      if (error !== null || response.statusCode !== 200) throw "Couldn't get song: " + songURL;
			      var $ = cheerio.load(body);
            var lyricsText = $(".lyrics_container .lyrics p").text();
            callback(null, {
			          songTitle: utilsRegex.obtainSongTitle($("h1.song_title a")["0"]["next"]["data"]),
				        trackNumber: $(".album_title_and_track_number").text().trim().split(" ")[1],
				        lyricsText: lyricsText,
				        songlyrics: lyricsText.split("\n")
            });
        });
    }

    // artist: function(artistName, callback) {
    //         url: $("[property='og:url']").attr("content"),
    //         name: $("[property='og:title']").attr("content")
    // }
};

// Process artist page

var getAlbums = function (albumURLs, callback) {
    async.parallel(_.map(albumURLs, function(albumURL) {
		    return buildfn(homeURL + albumURL);
    }), callback);
};

var buildfn = function(baseAlbumURL) {
	  // Closure invoked w/ async.parallel
	  var processAlbum = function (callback) {
		    console.log("processing album", baseAlbumURL);
        geniusQuery.albumData(baseAlbumURL, callback);
	  };
	  return processAlbum;
};

// process song(s)
var addSongs = function (year, albumTitle, $, callback) {
	  var processSongsTasks = [];
    $(".song_list .song_link").each(function() {
		    var songURL = (homeURL + this.attr("href"));
		    var track = new song(albumTitle, songURL, year);
		    var task = buildfn2(songURL, track);
		    processSongsTasks.push(task);
    });
    async.parallel(processSongsTasks, function (errors, results) {
		    console.log("All songs in " + albumTitle + " processed", errors, results);
		    callback(errors, results);
    });
};

var buildfn2 = function (songURL, track) {
	  // Closure invoked w/ async.parallel
	  var processSong = function (callback) {
		    console.log('processing song', songURL);
		    request(songURL, function (error, response, body) {
			      if (!error && response.statusCode == 200) {
			          var $ = cheerio.load(body);
			          var songTitle = utilsRegex.obtainSongTitle($("h1.song_title a")["0"]["next"]["data"]);
				        var trackNumber = $(".album_title_and_track_number").text().trim().split(" ")[1];
				        var lyricsText = $(".lyrics_container .lyrics p").text();
				        var songlyrics = lyricsText.split("\n");
				        track.addSongName(songTitle);
				        track.addTrackNumber(trackNumber);
				        track.addArtist(rapper.name);
				        track.addLyrics(songlyrics);
				        rapper.addSong(track);
				        callback(null);
			      } else {
				        console.log("Error retrieveing song details");
				        callback(error);
			      }
		    });
	  };
	  return processSong;
};

var getSongs = function(songURLs, callback) {
    async.parallel(_.map(songURLs, function(x) {
        return function(parallelCallback) {
            geniusQuery.songData(homeURL + x, parallelCallback);
        };
    }), function (errors, songs) {
		    callback(errors, songs);
    });
};

// Connect to DB
mongoose.connect("mongodb://localhost/rapgenius");
var homeURL = "http://rapgenius.com";
var rapper = new artist(process.argv[2] || "Skinnyman");
geniusQuery.albumURLs(rapper.name, function(error, albumURLs) {
    getAlbums(albumURLs, function(error, albums) {
        getSongs(_.pluck(albums, 'songURLs'), function(error, songs) {
				    process.exit(0);
        });
    });
});
<|endoftext|>


<commit_before>$(function(){

  // Reveal text overlays when hovering over a block
  // @Uses hoverIntent plugin: http://cherne.net/brian/resources/jquery.hoverIntent.html
  $(".overlay-wrap").hoverIntent(
    function() {
      var overlayheight = $(this).height() - 80;
      $(this).find(".overlay").stop().hide().css({
        'left' : 'auto',
        'minHeight' : overlayheight
      }).fadeIn(200);
    },
    function() {
      $(this).find(".overlay").stop().delay(300).fadeOut(600, function(){
        $(this).removeAttr('style');
      });
    }
  );

  // Add the read buttons
  $("button.read").clone().prependTo(".overlay-wrap");

  // Reveal overlays when buttons get focus (for keyboard navigation)
  $(".overlay-wrap .read").focus(function() {
    $(".overlay[style]").stop().delay(300).fadeOut(600, function(){   // First hide any visible overlays
      $(this).removeAttr('style');                                    // Then reset them to normal (hidden offscreen by CSS)
    });
    var overlayheight = $(this).parents(".overlay-wrap").height() - 80;;
    $(this).parents(".overlay-wrap").find(".overlay").hide().css({
      'left' : 'auto',
      'minHeight' : overlayheight
    }).fadeIn(200);
    $('html, body').animate({
      scrollTop: $(this).parents(".overlay-wrap").offset().top -40
    }, 300);
  });

  // Scroll the window
  var $window   = $(window);
  var $nav      = $('#page-nav');
  var $head     = $('#masthead');
  var navTop    = $nav.offset();
  var fixed     = false;
  var didScroll = false;

//  var mob    = $("#mobilized").waypoint();
//  var act    = $("#action").waypoint();

  $window.scroll(function() {
    didScroll = true;
  });

  $(document).ready(function() {
    var scrollTop = $window.scrollTop();
    if ( scrollTop >= navTop.top ) {
      didScroll = true;
    }
  });

  function adjustScrollbar() {
    if (didScroll) {
      didScroll = false;
      var scrollTop = $window.scrollTop();

      if( scrollTop >= navTop.top ) {
        if(!fixed) {
          fixed = true;
          $nav.addClass("fixed");
          $head.css({ "margin-bottom" : "60px" });
        }
      } else {
        if(fixed) {
          fixed = false;
          $nav.removeAttr("class");
          $head.css({ "margin-bottom" : "0" });
        }
      }
    }
  };

  $('#welcome').waypoint(function(event, direction) {
    if(fixed) {
      $nav.attr('class', 'fixed welcome');
      $nav.find("li").removeClass();
      $("#nav-welcome").addClass("current");
    }
  },{
    offset: 60
  });

  $('#mobilized').waypoint(function(event, direction) {
    if(fixed) {
      if (direction == 'down') {
        $nav.attr('class', 'fixed mobilized');
        $nav.find("li").removeClass();
        $("#nav-mobilized").addClass("current");
      }
      else {
        $nav.attr('class', 'fixed welcome');
        $nav.find("li").removeClass();
        $("#nav-welcome").addClass("current");
      }
    }
  },{
    offset: 60
  });

  $('#action').waypoint(function(event, direction) {
    if(fixed) {
      if (direction == 'down') {
        $nav.attr('class', 'fixed action');
        $nav.find("li").removeClass();
        $("#nav-action").addClass("current");
      }
      else {
        $nav.attr('class', 'fixed mobilized');
        $nav.find("li").removeClass();
        $("#nav-mobilized").addClass("current");
      }
    }
  },{
    offset: 60
  });

  $('#community').waypoint(function(event, direction) {
    if(fixed) {
      if (direction == 'down') {
        $nav.attr('class', 'fixed community');
        $nav.find("li").removeClass();
        $("#nav-community").addClass("current");
      }
      else {
        $nav.attr('class', 'fixed action');
        $nav.find("li").removeClass();
        $("#nav-action").addClass("current");
      }
    }
  },{
    offset: 60
  });

  $('#sustainability').waypoint(function(event, direction) {
    if(fixed) {
      if (direction == 'down') {
        $nav.attr('class', 'fixed sustainability');
        $nav.find("li").removeClass();
        $("#nav-sustainability").addClass("current");
      }
      else {
        $nav.attr('class', 'fixed community');
        $nav.find("li").removeClass();
        $("#nav-community").addClass("current");
      }
    }
  },{
    offset: 60
  });

  // Check for an adjusted scrollbar every 100ms.
  setInterval(adjustScrollbar, 100);

  // Bind scrolling to linked element.
  $window.on('click', '#page-nav a[href^="#"]', function(e) {
    e.preventDefault();
    // Extract the target element's ID from the link's href.
    var elem = $(this).attr("href").replace( /.*?(#.*)/g, "$1" );
    $('html, body').animate({
      scrollTop: $(elem).offset().top - 40
    }, 1000, function() {
      $(elem).attr('tabindex','100').focus().removeAttr('tabindex');
    });
  });

  // Load links in new tab/window
  $("a[rel='external']").click( function(e) {
    e.preventDefault();
    window.open(this.href);
  });

  // Load videos in a full-page modal
  $("a.video-play").click( function(e) {
    e.preventDefault();
    var origin = $(this);
    var video = $(this).attr("data-video-source");
    var poster = $(this).children("img").attr("src");
    $('body').addClass("noscroll").append('

<video id="video" poster="'+poster+'" controls autoplay></video>

');
    $("#video").append(
      '<source src="'+video+'.webm" type="video/webm">'
     +'<source src="'+video+'.mp4" type="video/mp4">'
    ).focus();
    $("#done").clone().appendTo("#inner");

    // Remove the full-page overlay
    $("#fill #done").click(function() {
      $("#video")[0].pause();
      $("#fill").remove();
      $("body").removeClass("noscroll");
      origin.focus();
    });

    $("#fill").keyup(function(e) {
      if (e.keyCode == 27) { // esc
        $("#fill").remove();
        $("body").removeClass("noscroll");
        origin.focus();
      }
    });

  });

  // Load the YouTube video in a full-page modal
  $("a.vid-youtube").click( function(e) {
    e.preventDefault();
    var origin = $(this);
    $('body').addClass("noscroll").append('

');
    $("#inner iframe").focus();
    $("#done").clone().appendTo("#inner");

    // Remove the full-page overlay
    $("#fill #done").click(function() {
      $("#fill").remove();
      $("body").removeClass("noscroll");
      origin.focus();
    });

  });

  // Contributor stories

  // Set up the carousel
  $('#story-slider').jcarousel({
    scroll: 1,
    initCallback: controlButtons,
    buttonNextHTML: null,
    buttonPrevHTML: null,
    itemLastOutCallback: { onAfterAnimation: disableButtons },
    itemLastInCallback: { onAfterAnimation: disableButtons }
  });

  // Add the control buttons
  $(".btn-prev, .btn-next").prependTo(".jcarousel-container");

  // Make the buttons work
  function controlButtons(carousel) {
    $('.btn-next').bind('click', function() {
      carousel.next();
    });
    $('.btn-prev').bind('click', function() {
      carousel.prev();
    });
  };

  // Disable the buttons at the end of the carousel
  function disableButtons(carousel){
    if (carousel.first == 1) {
      $('.btn-prev').attr('disabled','disabled').addClass('disabled');
    } else {
      $('.btn-prev').removeAttr('disabled').removeClass('disabled');
    }
    if (carousel.last == carousel.size()) {
      $('.btn-next').attr('disabled','disabled').addClass('disabled');
    } else {
      $('.btn-next').removeAttr('disabled').removeClass('disabled');
    }
  }

  // Set up contributor video
  var video   = $("#story-slider").find("a.contributor:first").attr("href");
  var poster  = $("#story-slider").find("a.contributor:first").attr("data-poster");
  var desc    = $("#story-slider").find(".vcard:first .note").html();
  $("#story-vid").attr('poster', poster).attr('src', video);
  $("#video-stage").append('
'+desc+'
');

  // Add a play button overlay
  $("#video-stage .player").append('');
  $("span.btn-play").click(function(){
    $("#story-vid").attr('controls','controls')[0].play();
    $(this).remove();
  })

  // Play contributor stories
  $("a.contributor").click( function(e) {
    e.preventDefault();
    var video   = $(this).attr("href");
    var poster  = $(this).attr("data-poster");
    var desc    = $(this).find(".note").html();
    if ($("#story-vid")[0].paused == false) {
      $("#story-vid")[0].pause();
    }
    if ( $("span.btn-play") ) {
      $("span.btn-play").remove();
    }
    $("#video-stage figcaption").html(desc);
    $("#story-vid").attr('poster', poster).attr('controls','controls').attr('src', video)[0].play();
  });

});

<commit_msg>Fixed JS error for FAQ page<commit_after>$(function(){

  // Reveal text overlays when hovering over a block
  // @Uses hoverIntent plugin: http://cherne.net/brian/resources/jquery.hoverIntent.html
  $(".overlay-wrap").hoverIntent(
    function() {
      var overlayheight = $(this).height() - 80;
      $(this).find(".overlay").stop().hide().css({
        'left' : 'auto',
        'minHeight' : overlayheight
      }).fadeIn(200);
    },
    function() {
      $(this).find(".overlay").stop().delay(300).fadeOut(600, function(){
        $(this).removeAttr('style');
      });
    }
  );

  // Add the read buttons
  $("button.read").clone().prependTo(".overlay-wrap");

  // Reveal overlays when buttons get focus (for keyboard navigation)
  $(".overlay-wrap .read").focus(function() {
    $(".overlay[style]").stop().delay(300).fadeOut(600, function(){   // First hide any visible overlays
      $(this).removeAttr('style');                                    // Then reset them to normal (hidden offscreen by CSS)
    });
    var overlayheight = $(this).parents(".overlay-wrap").height() - 80;;
    $(this).parents(".overlay-wrap").find(".overlay").hide().css({
      'left' : 'auto',
      'minHeight' : overlayheight
    }).fadeIn(200);
    $('html, body').animate({
      scrollTop: $(this).parents(".overlay-wrap").offset().top -40
    }, 300);
  });

  // Scroll the window
  var $window   = $(window);
  var $nav      = $('#page-nav');
  var $head     = $('#masthead');
  var navTop    = $nav.offset();
  var fixed     = false;
  var didScroll = false;

//  var mob    = $("#mobilized").waypoint();
//  var act    = $("#action").waypoint();

  $window.scroll(function() {
    didScroll = true;
  });

  $(document).ready(function() {
    var scrollTop = $window.scrollTop();
    if ( scrollTop >= navTop.top ) {
      didScroll = true;
    }
  });

  function adjustScrollbar() {
    if (didScroll) {
      didScroll = false;
      var scrollTop = $window.scrollTop();

      if( scrollTop >= navTop.top ) {
        if(!fixed) {
          fixed = true;
          $nav.addClass("fixed");
          $head.css({ "margin-bottom" : "60px" });
        }
      } else {
        if(fixed) {
          fixed = false;
          $nav.removeAttr("class");
          $head.css({ "margin-bottom" : "0" });
        }
      }
    }
  };

  $('#welcome').waypoint(function(event, direction) {
    if(fixed) {
      $nav.attr('class', 'fixed welcome');
      $nav.find("li").removeClass();
      $("#nav-welcome").addClass("current");
    }
  },{
    offset: 60
  });

  $('#mobilized').waypoint(function(event, direction) {
    if(fixed) {
      if (direction == 'down') {
        $nav.attr('class', 'fixed mobilized');
        $nav.find("li").removeClass();
        $("#nav-mobilized").addClass("current");
      }
      else {
        $nav.attr('class', 'fixed welcome');
        $nav.find("li").removeClass();
        $("#nav-welcome").addClass("current");
      }
    }
  },{
    offset: 60
  });

  $('#action').waypoint(function(event, direction) {
    if(fixed) {
      if (direction == 'down') {
        $nav.attr('class', 'fixed action');
        $nav.find("li").removeClass();
        $("#nav-action").addClass("current");
      }
      else {
        $nav.attr('class', 'fixed mobilized');
        $nav.find("li").removeClass();
        $("#nav-mobilized").addClass("current");
      }
    }
  },{
    offset: 60
  });

  $('#community').waypoint(function(event, direction) {
    if(fixed) {
      if (direction == 'down') {
        $nav.attr('class', 'fixed community');
        $nav.find("li").removeClass();
        $("#nav-community").addClass("current");
      }
      else {
        $nav.attr('class', 'fixed action');
        $nav.find("li").removeClass();
        $("#nav-action").addClass("current");
      }
    }
  },{
    offset: 60
  });

  $('#sustainability').waypoint(function(event, direction) {
    if(fixed) {
      if (direction == 'down') {
        $nav.attr('class', 'fixed sustainability');
        $nav.find("li").removeClass();
        $("#nav-sustainability").addClass("current");
      }
      else {
        $nav.attr('class', 'fixed community');
        $nav.find("li").removeClass();
        $("#nav-community").addClass("current");
      }
    }
  },{
    offset: 60
  });

  // Check for an adjusted scrollbar every 100ms.
  setInterval(adjustScrollbar, 100);

  // Bind scrolling to linked element.
  $window.on('click', '#page-nav a[href^="#"]', function(e) {
    e.preventDefault();
    // Extract the target element's ID from the link's href.
    var elem = $(this).attr("href").replace( /.*?(#.*)/g, "$1" );
    $('html, body').animate({
      scrollTop: $(elem).offset().top - 40
    }, 1000, function() {
      $(elem).attr('tabindex','100').focus().removeAttr('tabindex');
    });
  });

  // Load links in new tab/window
  $("a[rel='external']").click( function(e) {
    e.preventDefault();
    window.open(this.href);
  });

  // Load videos in a full-page modal
  $("a.video-play").click( function(e) {
    e.preventDefault();
    var origin = $(this);
    var video = $(this).attr("data-video-source");
    var poster = $(this).children("img").attr("src");
    $('body').addClass("noscroll").append('

<video id="video" poster="'+poster+'" controls autoplay></video>

');
    $("#video").append(
      '<source src="'+video+'.webm" type="video/webm">'
     +'<source src="'+video+'.mp4" type="video/mp4">'
    ).focus();
    $("#done").clone().appendTo("#inner");

    // Remove the full-page overlay
    $("#fill #done").click(function() {
      $("#video")[0].pause();
      $("#fill").remove();
      $("body").removeClass("noscroll");
      origin.focus();
    });

    $("#fill").keyup(function(e) {
      if (e.keyCode == 27) { // esc
        $("#fill").remove();
        $("body").removeClass("noscroll");
        origin.focus();
      }
    });

  });

  // Load the YouTube video in a full-page modal
  $("a.vid-youtube").click( function(e) {
    e.preventDefault();
    var origin = $(this);
    $('body').addClass("noscroll").append('

');
    $("#inner iframe").focus();
    $("#done").clone().appendTo("#inner");

    // Remove the full-page overlay
    $("#fill #done").click(function() {
      $("#fill").remove();
      $("body").removeClass("noscroll");
      origin.focus();
    });

  });

  // Contributor stories
  // Set up the carousel
  $('#story-slider').jcarousel({
    scroll: 1,
    initCallback: controlButtons,
    buttonNextHTML: null,
    buttonPrevHTML: null,
    itemLastOutCallback: { onAfterAnimation: disableButtons },
    itemLastInCallback: { onAfterAnimation: disableButtons }
  });

  // Add the control buttons
  if ($('#story-slider').jcarousel()) {
    $(".btn-prev, .btn-next").prependTo(".jcarousel-container");
  }

  // Make the buttons work
  function controlButtons(carousel) {
    $('.btn-next').bind('click', function() {
      carousel.next();
    });
    $('.btn-prev').bind('click', function() {
      carousel.prev();
    });
  };

  // Disable the buttons at the end of the carousel
  function disableButtons(carousel){
    if (carousel.first == 1) {
      $('.btn-prev').attr('disabled','disabled').addClass('disabled');
    } else {
      $('.btn-prev').removeAttr('disabled').removeClass('disabled');
    }
    if (carousel.last == carousel.size()) {
      $('.btn-next').attr('disabled','disabled').addClass('disabled');
    } else {
      $('.btn-next').removeAttr('disabled').removeClass('disabled');
    }
  }

  // Set up contributor video
  if ($("#video-stage").length != 0) {
    var video   = $("#story-slider").find("a.contributor:first").attr("href");
    var poster  = $("#story-slider").find("a.contributor:first").attr("data-poster");
    var desc    = $("#story-slider").find(".vcard:first .note").html();
    $("#story-vid").attr('poster', poster).attr('src', video);
    $("#video-stage").append('
'+desc+'
');
  }

  // Add a play button overlay
  $("#video-stage .player").append('');
  $("span.btn-play").click(function(){
    $("#story-vid").attr('controls','controls')[0].play();
    $(this).remove();
  })

  // Play contributor stories
  $("a.contributor").click( function(e) {
    e.preventDefault();
    var video   = $(this).attr("href");
    var poster  = $(this).attr("data-poster");
    var desc    = $(this).find(".note").html();
    if ($("#story-vid")[0].paused == false) {
      $("#story-vid")[0].pause();
    }
    if ( $("span.btn-play") ) {
      $("span.btn-play").remove();
    }
    $("#video-stage figcaption").html(desc);
    $("#story-vid").attr('poster', poster).attr('controls','controls').attr('src', video)[0].play();
  });

});

<|endoftext|>


<commit_before>const { Pangu } = require('../shared/core');

function once(func) {
  let executed = false;
  return () => {
    if (executed) {
      return;
    }
    const self = this;
    executed = true;
    func.apply(self, arguments);
  };
}

function debounce(func, delay, mustRunDelay) {
  let timer = null;
  let startTime = null;
  return () => {
    const self = this;
    const args = arguments;
    const currentTime = + new Date();

    clearTimeout(timer);

    if (!startTime) {
      startTime = currentTime;
    }

    if (currentTime - startTime >= mustRunDelay) {
      func.apply(self, args);
      startTime = currentTime;
    } else {
      timer = setTimeout(() => {
        func.apply(self, args);
      }, delay);
    }
  };
}

// https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType

class BrowserPangu extends Pangu {
  constructor() {
    super();

    this.blockTags = /^(div|p|h1|h2|h3|h4|h5|h6)$/i;
    this.ignoredTags = /^(script|code|pre|textarea)$/i;
    this.presentationalTags = /^(b|code|del|em|i|s|strong)$/i;
    this.spaceLikeTags = /^(br|hr|i|img|pangu)$/i;
    this.spaceSensitiveTags = /^(a|del|pre|s|strike|u)$/i;

    this.isAutoSpacingPageExecuted = false;

    // TODO
    // this.ignoredTags adds iframe|pangu
    // this.ignoreClasses
    // this.ignoreAttributes
  }

  isContentEditable(node) {
    return ((node.isContentEditable) || (node.getAttribute && node.getAttribute('g_editable') === 'true'));
  }

  isSpecificTag(node, tagRegex) {
    return (node && node.nodeName && node.nodeName.search(tagRegex) >= 0);
  }

  isInsideSpecificTag(node, tagRegex, checkCurrent = false) {
    let currentNode = node;
    if (checkCurrent) {
      if (this.isSpecificTag(currentNode, tagRegex)) {
        return true;
      }
    }
    while (currentNode.parentNode) {
      currentNode = currentNode.parentNode;
      if (this.isSpecificTag(currentNode, tagRegex)) {
        return true;
      }
    }
    return false;
  }

  canIgnoreNode(node) {
    let currentNode = node;
    if (currentNode && (this.isSpecificTag(currentNode, this.ignoredTags) || this.isContentEditable(currentNode))) {
      return true;
    }
    while (currentNode.parentNode) {
      currentNode = currentNode.parentNode;
      if (currentNode && (this.isSpecificTag(currentNode, this.ignoredTags) || this.isContentEditable(currentNode))) {
        return true;
      }
    }
    return false;
  }

  isFirstTextChild(parentNode, targetNode) {
    const { childNodes } = parentNode;

    // 只判斷第一個含有 text 的 node
    for (let i = 0; i < childNodes.length; i++) {
      const childNode = childNodes[i];
      if (childNode.nodeType !== Node.COMMENT_NODE && childNode.textContent) {
        return childNode === targetNode;
      }
    }
    return false;
  }

  isLastTextChild(parentNode, targetNode) {
    const { childNodes } = parentNode;

    // 只判斷倒數第一個含有 text 的 node
    for (let i = childNodes.length - 1; i > -1; i--) {
      const childNode = childNodes[i];
      if (childNode.nodeType !== Node.COMMENT_NODE && childNode.textContent) {
        return childNode === targetNode;
      }
    }
    return false;
  }

  spacingNodeByXPath(xPathQuery, contextNode) {
    // 因為 xPathQuery 會是用 text() 結尾，所以這些 nodes 會是 text 而不是 DOM element
    // snapshotLength 要配合 XPathResult.ORDERED_NODE_SNAPSHOT_TYPE 使用
    // https://developer.mozilla.org/en-US/docs/DOM/document.evaluate
    // https://developer.mozilla.org/en-US/docs/Web/API/XPathResult
    const textNodes = document.evaluate(xPathQuery, contextNode, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);

    let currentTextNode;
    let nextTextNode;

    // 從最下面、最裡面的節點開始，所以是倒序的
    for (let i = textNodes.snapshotLength - 1; i > -1; --i) {
      currentTextNode = textNodes.snapshotItem(i);

      if (this.isSpecificTag(currentTextNode.parentNode, this.presentationalTags) && !this.isInsideSpecificTag(currentTextNode.parentNode, this.ignoredTags)) {
        const elementNode = currentTextNode.parentNode;

        if (elementNode.previousSibling) {
          const { previousSibling } = elementNode;
          if (previousSibling.nodeType === Node.TEXT_NODE) {
            const testText = previousSibling.data.substr(-1) + currentTextNode.data.toString().charAt(0);
            const testNewText = this.spacing(testText);
            if (testText !== testNewText) {
              previousSibling.data = `${previousSibling.data} `;
            }
          }
        }

        if (elementNode.nextSibling) {
          const { nextSibling } = elementNode;
          if (nextSibling.nodeType === Node.TEXT_NODE) {
            const testText = currentTextNode.data.substr(-1) + nextSibling.data.toString().charAt(0);
            const testNewText = this.spacing(testText);
            if (testText !== testNewText) {
              nextSibling.data = ` ${nextSibling.data}`;
            }
          }
        }
      }

      if (this.canIgnoreNode(currentTextNode)) {
        nextTextNode = currentTextNode;
        continue;
      }

      const newText = this.spacing(currentTextNode.data);
      if (currentTextNode.data !== newText) {
        currentTextNode.data = newText;
      }

      // 處理嵌套的 <tag> 中的文字
      if (nextTextNode) {
        // TODO
        // 現在只是簡單地判斷相鄰的下一個 node 是不是

        // 萬一遇上嵌套的標籤就不行了
        if (currentTextNode.nextSibling && currentTextNode.nextSibling.nodeName.search(this.spaceLikeTags) >= 0) {
          nextTextNode = currentTextNode;
          continue;
        }

        // currentTextNode 的最後一個字 + nextTextNode 的第一個字
        const testText = currentTextNode.data.toString().substr(-1) + nextTextNode.data.toString().substr(0, 1);
        const testNewText = this.spacing(testText);
        if (testNewText !== testText) {
          // 往上找 nextTextNode 的 parent node
          // 直到遇到 spaceSensitiveTags
          // 而且 nextTextNode 必須是第一個 text child
          // 才能把空格加在 nextTextNode 的前面
          let nextNode = nextTextNode;
          while (nextNode.parentNode && nextNode.nodeName.search(this.spaceSensitiveTags) === -1 && this.isFirstTextChild(nextNode.parentNode, nextNode)) {
            nextNode = nextNode.parentNode;
          }

          let currentNode = currentTextNode;
          while (currentNode.parentNode && currentNode.nodeName.search(this.spaceSensitiveTags) === -1 && this.isLastTextChild(currentNode.parentNode, currentNode)) {
            currentNode = currentNode.parentNode;
          }

          if (currentNode.nextSibling) {
            if (currentNode.nextSibling.nodeName.search(this.spaceLikeTags) >= 0) {
              nextTextNode = currentTextNode;
              continue;
            }
          }

          if (currentNode.nodeName.search(this.blockTags) === -1) {
            if (nextNode.nodeName.search(this.spaceSensitiveTags) === -1) {
              if ((nextNode.nodeName.search(this.ignoredTags) === -1) && (nextNode.nodeName.search(this.blockTags) === -1)) {
                if (nextTextNode.previousSibling) {
                  if (nextTextNode.previousSibling.nodeName.search(this.spaceLikeTags) === -1) {
                    nextTextNode.data = ` ${nextTextNode.data}`;
                  }
                } else {
                  // dirty hack
                  if (!this.canIgnoreNode(nextTextNode)) {
                    nextTextNode.data = ` ${nextTextNode.data}`;
                  }
                }
              }
            } else if (currentNode.nodeName.search(this.spaceSensitiveTags) === -1) {
              currentTextNode.data = `${currentTextNode.data} `;
            } else {
              const panguSpace = document.createElement('pangu');
              panguSpace.innerHTML = ' ';

              // 避免一直被加空格
              if (nextNode.previousSibling) {
                if (nextNode.previousSibling.nodeName.search(this.spaceLikeTags) === -1) {
                  nextNode.parentNode.insertBefore(panguSpace, nextNode);
                }
              } else {
                nextNode.parentNode.insertBefore(panguSpace, nextNode);
              }

              // TODO
              // 主要是想要避免在元素（通常都是
）的開頭加空格
              // 這個做法有點蠢，但是不管還是先硬上
              if (!panguSpace.previousElementSibling) {
                if (panguSpace.parentNode) {
                  panguSpace.parentNode.removeChild(panguSpace);
                }
              }
            }
          }
        }
      }

      nextTextNode = currentTextNode;
    }
  }

  spacingNode(contextNode) {
    let xPathQuery = './/*/text()[normalize-space(.)]';
    if (contextNode.children && contextNode.children.length === 0) {
      xPathQuery = './/text()[normalize-space(.)]';
    }
    this.spacingNodeByXPath(xPathQuery, contextNode);
  }

  spacingElementById(idName) {
    const xPathQuery = `id("${idName}")//text()`;
    this.spacingNodeByXPath(xPathQuery, document);
  }

  spacingElementByClassName(className) {
    const xPathQuery = `//*[contains(concat(" ", normalize-space(@class), " "), "${className}")]//text()`;
    this.spacingNodeByXPath(xPathQuery, document);
  }

  spacingElementByTagName(tagName) {
    const xPathQuery = `//${tagName}//text()`;
    this.spacingNodeByXPath(xPathQuery, document);
  }

  spacingPageTitle() {
    const xPathQuery = '/html/head/title/text()';
    this.spacingNodeByXPath(xPathQuery, document);
  }

  spacingPageBody() {
    // // >> 任意位置的節點
    // . >> 當前節點
    // .. >> 父節點
    // [] >> 條件
    // text() >> 節點的文字內容，例如 hello 之於 <tag>hello</tag>
    // https://www.w3schools.com/xml/xpath_syntax.asp
    //
    // [@contenteditable]
    // 帶有 contenteditable 屬性的節點
    //
    // normalize-space(.)
    // 當前節點的頭尾的空白字元都會被移除，大於兩個以上的空白字元會被置換成單一空白
    // https://developer.mozilla.org/en-US/docs/Web/XPath/Functions/normalize-space
    //
    // name(..)
    // 父節點的名稱
    // https://developer.mozilla.org/en-US/docs/Web/XPath/Functions/name
    //
    // translate(string, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz")
    // 將 string 轉換成小寫，因為 XML 是 case-sensitive 的
    // https://developer.mozilla.org/en-US/docs/Web/XPath/Functions/translate
    //
    // 1. 處理 <title>
    // 2. 處理  底下的節點
    // 3. 略過 contentEditable 的節點
    // 4. 略過特定節點，例如


<commit_before>/*
 * (C) Copyright 2013 Kurento (http://kurento.org/)
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser General Public License
 * (LGPL) version 2.1 which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/lgpl-2.1.html
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 */

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

/**
 * @constructor
 *
 * @param {String} url: URL of the WebRTC endpoint server.
 * @param {Object} options: optional configuration parameters
 *   {Enum('inactive', 'sendonly', 'recvonly', 'sendrecv')} audio: audio stream mode
 *   {Enum('inactive', 'sendonly', 'recvonly', 'sendrecv')} video: video stream mode
 *   {[Object]} iceServers: array of objects to initialize the ICE servers. It
 *     structure is the same as an Array of WebRTC RTCIceServer objects.
 *
 * @throws RangeError
 */
function KwsWebRtcContent(url, options)
{
  options = options || {};

  Content.call(this, url, options);

  var self = this;

  var pc = null;

  function onerror(error)
  {
    self.emit('error', error);
  };

  /**
   * Request a connection with the webRTC endpoint server
   *
   * @private
   *
   * @param {MediaStream | undefined} localStream: stream locally offered
   */
  function initRtc(localStream)
  {
    // Create the PeerConnection object
    var iceServers = options.iceServers
                  || [{url: 'stun:'+'stun.l.google.com:19302'}];

    pc = new RTCPeerConnection({iceServers: iceServers});

    // Add the local stream if defined
    if(localStream)
      pc.addStream(localStream);

    // Dispatch 'close' event if signaling gets closed
    pc.onsignalingstatechange = function(event)
    {
      if(pc.signalingState == "closed")
        self.emit('terminate');
    };

    var mediaConstraints =
    {
      mandatory:
      {
        OfferToReceiveAudio: self._audio.remote,
        OfferToReceiveVideo: self._video.remote
      }
    };

    pc.createOffer(function(offer)
    {
      // Set the peer local description
      pc.setLocalDescription(offer,
      function()
      {
        console.info("LocalDescription correctly set");
      },
      onerror);
    },
    onerror,
    mediaConstraints);

    pc.onicecandidate = function(event)
    {
      // We are still generating the candidates, don't send the SDP yet.
      if(event.candidate)
        return;

      start();
    }
  }

  // RPC calls

  // Start

  /**
   * Request a connection with the webRTC endpoint server
   *
   * @private
   */
  function start()
  {
    /**
     * Callback when connection is succesful
     *
     * @private
     *
     * @param {Object} response: JsonRPC response
     */
    function success(result)
    {
      function success2()
      {
        // Local streams
        if(self._video.local)
        {
          var streams = pc.getLocalStreams();

          if(streams && streams[0])
          {
            var stream = streams[0];
            var url = URL.createObjectURL(stream);

            if(options.localVideoTag)
            {
              var localVideo = document.getElementById(options.localVideoTag);

              if(localVideo)
                 localVideo.src = url;
              else
              {
                var msg = "Requested local video tag '"+options.localVideoTag
                        + "' is not available";
                onerror(new Error(msg));
                return
              };
            };

            self.emit('localstream', {stream: stream, url: url});
          }
          else
          {
            onerror(new Error("No local streams are available"));
            return
          }
        };

        // Remote streams
        if(self._video.remote)
        {
          var streams = pc.getRemoteStreams();

          if(streams && streams[0])
          {
            var stream = streams[0];
            var url = URL.createObjectURL(stream);

            if(options.remoteVideoTag)
            {
              var remoteVideo = self._setRemoteVideoTag(url);

            }
            else
              console.warn("No remote video tag available, successful terminate event due to remote end will be no dispatched");

            self.emit('remotestream', {stream: stream, url: url});
          }
          else
          {
            self.emit('error', new Error("No remote streams are available"));
            return
          }
        };

        // Notify we created the connection successfully
        self.emit('start');
      };

      // Set answer description and init local environment
      pc.setRemoteDescription(new RTCSessionDescription(
      {
        type: 'answer',
        sdp: result.sdp
      }),
      success2,
      onerror);
    };

    var params =
    {
      sdp: pc.localDescription.sdp,
      constraints:
      {
        audio: options.audio,
        video: options.video
      }
    };

    self._start(params, success);
  };

  /**
   * Terminate the connection with the WebRTC media server
   */
  function close()
  {
    // Close the PeerConnection
    pc.close();
  };

  this.on('error',     close);
  this.on('terminate', close);

  // Mode set to send local audio and/or video stream
  if(this._audio.local || this._video.local)
    getUserMedia({'audio': this._audio.local, 'video': this._video.local},
    function(stream)
    {
      console.log('User has granted access to local media.');

      initRtc(stream);
    },
    onerror);

  // Mode set to only receive a stream, not send it
  else
    initRtc();
};
KwsWebRtcContent.prototype.__proto__   = Content.prototype;
KwsWebRtcContent.prototype.constructor = KwsWebRtcContent;

module.exports = KwsWebRtcContent;<commit_msg>Muted local video and enabled optional DtlsSrtpKeyAgreement flag<commit_after>/*
 * (C) Copyright 2013 Kurento (http://kurento.org/)
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser General Public License
 * (LGPL) version 2.1 which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/lgpl-2.1.html
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 */

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

/**
 * @constructor
 *
 * @param {String} url: URL of the WebRTC endpoint server.
 * @param {Object} options: optional configuration parameters
 *   {Enum('inactive', 'sendonly', 'recvonly', 'sendrecv')} audio: audio stream mode
 *   {Enum('inactive', 'sendonly', 'recvonly', 'sendrecv')} video: video stream mode
 *   {[Object]} iceServers: array of objects to initialize the ICE servers. It
 *     structure is the same as an Array of WebRTC RTCIceServer objects.
 *
 * @throws RangeError
 */
function KwsWebRtcContent(url, options)
{
  options = options || {};

  Content.call(this, url, options);

  var self = this;

  var pc = null;

  function onerror(error)
  {
    self.emit('error', error);
  };

  /**
   * Request a connection with the webRTC endpoint server
   *
   * @private
   *
   * @param {MediaStream | undefined} localStream: stream locally offered
   */
  function initRtc(localStream)
  {
    // Create the PeerConnection object
    var iceServers = options.iceServers
                  || [{url: 'stun:'+'stun.l.google.com:19302'}];

    pc = new RTCPeerConnection
    (
      {iceServers: iceServers},
      {optional: [{DtlsSrtpKeyAgreement: true}]}
    );

    // Add the local stream if defined
    if(localStream)
      pc.addStream(localStream);

    // Dispatch 'close' event if signaling gets closed
    pc.onsignalingstatechange = function(event)
    {
      if(pc.signalingState == "closed")
        self.emit('terminate');
    };

    var mediaConstraints =
    {
      mandatory:
      {
        OfferToReceiveAudio: self._audio.remote,
        OfferToReceiveVideo: self._video.remote
      }
    };

    pc.createOffer(function(offer)
    {
      // Set the peer local description
      pc.setLocalDescription(offer,
      function()
      {
        console.info("LocalDescription correctly set");
      },
      onerror);
    },
    onerror,
    mediaConstraints);

    pc.onicecandidate = function(event)
    {
      // We are still generating the candidates, don't send the SDP yet.
      if(event.candidate)
        return;

      start();
    }
  }

  // RPC calls

  // Start

  /**
   * Request a connection with the webRTC endpoint server
   *
   * @private
   */
  function start()
  {
    /**
     * Callback when connection is succesful
     *
     * @private
     *
     * @param {Object} response: JsonRPC response
     */
    function success(result)
    {
      function success2()
      {
        // Local streams
        if(self._video.local)
        {
          var streams = pc.getLocalStreams();

          if(streams && streams[0])
          {
            var stream = streams[0];
            var url = URL.createObjectURL(stream);

            if(options.localVideoTag)
            {
              var localVideo = document.getElementById(options.localVideoTag);

              if(localVideo)
              {
                localVideo.muted = true;
                localVideo.src = url;
              }
              else
              {
                var msg = "Requested local video tag '"+options.localVideoTag
                        + "' is not available";
                onerror(new Error(msg));
                return
              };
            };

            self.emit('localstream', {stream: stream, url: url});
          }
          else
          {
            onerror(new Error("No local streams are available"));
            return
          }
        };

        // Remote streams
        if(self._video.remote)
        {
          var streams = pc.getRemoteStreams();

          if(streams && streams[0])
          {
            var stream = streams[0];
            var url = URL.createObjectURL(stream);

            if(options.remoteVideoTag)
            {
              var remoteVideo = self._setRemoteVideoTag(url);

            }
            else
              console.warn("No remote video tag available, successful terminate event due to remote end will be no dispatched");

            self.emit('remotestream', {stream: stream, url: url});
          }
          else
          {
            self.emit('error', new Error("No remote streams are available"));
            return
          }
        };

        // Notify we created the connection successfully
        self.emit('start');
      };

      // Set answer description and init local environment
      pc.setRemoteDescription(new RTCSessionDescription(
      {
        type: 'answer',
        sdp: result.sdp
      }),
      success2,
      onerror);
    };

    var params =
    {
      sdp: pc.localDescription.sdp,
      constraints:
      {
        audio: options.audio,
        video: options.video
      }
    };

    self._start(params, success);
  };

  /**
   * Terminate the connection with the WebRTC media server
   */
  function close()
  {
    // Close the PeerConnection
    pc.close();
  };

  this.on('error',     close);
  this.on('terminate', close);

  // Mode set to send local audio and/or video stream
  if(this._audio.local || this._video.local)
    getUserMedia({'audio': this._audio.local, 'video': this._video.local},
    function(stream)
    {
      console.log('User has granted access to local media.');

      initRtc(stream);
    },
    onerror);

  // Mode set to only receive a stream, not send it
  else
    initRtc();
};
KwsWebRtcContent.prototype.__proto__   = Content.prototype;
KwsWebRtcContent.prototype.constructor = KwsWebRtcContent;

module.exports = KwsWebRtcContent;<|endoftext|>


<commit_before>const http = require('http');

const notFound = (res) => {
  res.statusCode = 404;
  res.end();
};

const movedPermanently = (res, location) => {
  res.statusCode = 301;
  res.setHeader('Location', location);
  res.end();
};

const getNewLocation = (urlArr, isHttps, canWebp) => {
  const newUri = urlArr.slice(2).join('/');
  return `http${isHttps ? 's' : ''}://dn-tatpics.qbox.me/${newUri}!normal${canWebp ? '.webp' : ''}`;
};

const server = http.createServer((req, res) => {
  const urlArr = req.url.split('/');
  const referer = req.headers['referer'] || '';
  const isHttps = referer.indexOf('https://') === 0;
  const accept = req.headers['accept'] || '';
  const webp = accept.indexOf('webp') !== -1;
  if (urlArr[1] !== 'v') return notFound(res);
  return movedPermanently(res, getNewLocation(urlArr, isHttps, webp));
});

server.listen(2333, () => console.log('serverStart'));<commit_msg>All http<commit_after>const http = require('http');

const notFound = (res) => {
  res.statusCode = 404;
  res.end();
};

const movedPermanently = (res, location) => {
  res.statusCode = 301;
  res.setHeader('Location', location);
  res.end();
};

const getNewLocation = (urlArr, canWebp) => {
  const newUri = urlArr.slice(2).join('/');
  return `https://dn-tatpics.qbox.me/${newUri}!normal${canWebp ? '.webp' : ''}`;
};

const server = http.createServer((req, res) => {
  const urlArr = req.url.split('/');
  const accept = req.headers['accept'] || '';
  const webp = accept.indexOf('webp') !== -1;
  if (urlArr[1] !== 'v') return notFound(res);
  return movedPermanently(res, getNewLocation(urlArr, webp));
});

server.listen(2333, () => console.log('serverStart'));<|endoftext|>


<commit_before>var nconf = require('nconf');
var request = require('request');
var _ = require('underscore');
var Q = require('q');
var ADNStream = require('./adnstream').ADNStream;

var api_url_base = nconf.get('adn:api_url_base') || 'https://alpha-api.app.net';
// There are some comments in auth.js which explain this mess
var api_host_override = nconf.get('adn:api_host_override');

var key = nconf.get('adn:stream_key') || 'pm_stream';
var filter_id = nconf.get('adn:stream_filter_id') || 545;

function StreamRouter(app, lightpoll) {
    this.app = app;
    this.lightpoll = lightpoll;
}

StreamRouter.prototype.get_or_create_stream = function (app_access_token, cb) {
    var stream_url = api_url_base + '/stream/0/streams';

    var headers = {
        authorization: 'Bearer ' + app_access_token,
        host: api_host_override
    };

    var stream_template = {
        object_types: ["message", "channel", "stream_marker", "channel_subscription"],
        type: "long_poll",
        key: key,
        filter_id: filter_id
    };

    var create_stream = function () {
        request.post({
            url: stream_url,
            headers: headers,
            json: stream_template
        }, function (e, r, body) {
            if (!e && r.statusCode === 200) {
                console.log('created stream id ' +  body.data.id);
                cb(body.data.endpoint);
            } else {
                console.log('Error getting creating stream', e, 'status', r.statusCode, 'body', body);
                cb();
            }
        });
    };

    request.get({
        url: stream_url,
        qs: {
            key: key
        },
        headers: headers
    }, function (e, r, body) {
        if (!e && r.statusCode === 200) {
            var obj = JSON.parse(body);
            if (obj.data.length) {
                var stream = obj.data[0];
                var found_stream = _.pick(stream, _.keys(stream_template));
                found_stream['filter_id'] = stream.filter && stream.filter.id;

                console.log(found_stream);
                console.log('---');
                console.log(stream);
                console.log('---');
                console.log(stream_template);
                console.log('---');

                if (_.isEqual(found_stream, stream_template)) {
                    console.log('reusing stream id', stream.id);
                    cb(stream.endpoint);
                } else {
                    // delete stream so we can recreate it with correct template
                    request.del({
                        url: stream_url + '/' + stream.id,
                        headers: headers
                    }, function (e, r, body) {
                        if (!e && r.statusCode === 200){
                            create_stream();
                        } else {
                            console.log('Error deleting stream ' + stream.id);
                            cb();
                        }
                    });
                }
            } else {
                create_stream();
            }
        } else {
            console.log('Error getting stream', e, 'status', r.statusCode, 'body', body);
            cb();
        }
    });
};

StreamRouter.prototype.stream = function (token) {
    var self = this;

    var listen_to_endpoint = function (app_token, endpoint) {
        var stream = new ADNStream(endpoint);

        var channel_subs_cache = {};
        var in_flight_request_promises = {};

        stream.on('channel_subscription', function (msg) {
            channel_id = msg.data.channel.id;

            // only accept incremental updates for channels
            // we have seen and received a server response for
            Q.when(in_flight_request_promises[channel_id], function () {
                if (channel_subs_cache[channel_id]) {
                    if (msg.meta.deleted) {
                        channel_subs_cache[channel_id] = _.without(channel_subs_cache[channel_id], msg.data.user.id);
                    } else {
                        channel_subs_cache[channel_id].push(msg.data.user.id);
                    }
                }
            });
        });

        var get_users_for_channel = function (channel_id) {
            if (in_flight_request_promises[channel_id]) {
                return in_flight_request_promises[channel_id];
            }

            if (!channel_subs_cache[channel_id]) {
                var deferred = Q.defer();
                in_flight_request_promises[channel_id] = deferred.promise;

                var headers = {
                    authorization: 'Bearer ' + app_token,
                    host: api_host_override
                };

                request.get({
                    url: api_url_base + '/stream/0/channels/' + channel_id + '/subscribers/ids',
                    headers: headers
                }, function (e, r, body) {
                    if (!e && r.statusCode === 200) {
                        var ids = JSON.parse(body).data || [];
                        channel_subs_cache[channel_id] = ids;
                        delete in_flight_request_promises[channel_id];
                        deferred.resolve(ids);
                    } else {
                        if (!e) {
                            e = 'Unexpected response code: ' + r.statusCode + ' ' + r.request.url;
                        }

                        // If there was an error, dump the cache so we go back
                        // and refresh the full list again.
                        delete channel_subs_cache[channel_id];
                        delete in_flight_request_promises[channel_id];
                        deferred.reject(new Error (e));
                    }
                });

                return deferred.promise;
            }

            return channel_subs_cache[channel_id];
        };

        var send_message_to_channel = function (msg, channel_id) {
            Q.when(get_users_for_channel(channel_id), function (user_ids) {
                _.each(user_ids, function (user_id) {
                    self.lightpoll.dispatch(user_id, msg);
                });
            }, function (error) {
                console.log('error here', error);
            });
        };

        stream.on('channel', function (msg) {
            send_message_to_channel(msg, msg.meta.id);
        });

        stream.on('stream_marker', function (msg) {
            // currently we get *all* stream markers
            if (msg.meta.channel_id) {
                send_message_to_channel(msg, msg.meta.channel_id);
            }
        });

        stream.on('message', function (msg) {
            send_message_to_channel(msg, msg.data.channel_id);
        });

        stream.on('error', function (error) {
            console.error('Stream error, reconnecting:', error);
            setTimeout(function () {
                stream.process(true);
            }, 2000);
        });

        stream.on('end', function () {
            console.error('Stream ended, reconnecting.');
            setTimeout(function () {
                stream.process(true);
            }, 2000);
        });

        stream.process(true);
    };

    self.get_or_create_stream(token, function (endpoint) {
        listen_to_endpoint(token, endpoint);
    });
};

module.exports.create_router = function (app, lightpoll) {
    return new StreamRouter(app, lightpoll);
};
<commit_msg>OK, don't log this anymore<commit_after>var nconf = require('nconf');
var request = require('request');
var _ = require('underscore');
var Q = require('q');
var ADNStream = require('./adnstream').ADNStream;

var api_url_base = nconf.get('adn:api_url_base') || 'https://alpha-api.app.net';
// There are some comments in auth.js which explain this mess
var api_host_override = nconf.get('adn:api_host_override');

var key = nconf.get('adn:stream_key') || 'pm_stream';
var filter_id = nconf.get('adn:stream_filter_id') || 545;

function StreamRouter(app, lightpoll) {
    this.app = app;
    this.lightpoll = lightpoll;
}

StreamRouter.prototype.get_or_create_stream = function (app_access_token, cb) {
    var stream_url = api_url_base + '/stream/0/streams';

    var headers = {
        authorization: 'Bearer ' + app_access_token,
        host: api_host_override
    };

    var stream_template = {
        object_types: ["message", "channel", "stream_marker", "channel_subscription"],
        type: "long_poll",
        key: key,
        filter_id: filter_id
    };

    var create_stream = function () {
        request.post({
            url: stream_url,
            headers: headers,
            json: stream_template
        }, function (e, r, body) {
            if (!e && r.statusCode === 200) {
                console.log('created stream id ' +  body.data.id);
                cb(body.data.endpoint);
            } else {
                console.log('Error getting creating stream', e, 'status', r.statusCode, 'body', body);
                cb();
            }
        });
    };

    request.get({
        url: stream_url,
        qs: {
            key: key
        },
        headers: headers
    }, function (e, r, body) {
        if (!e && r.statusCode === 200) {
            var obj = JSON.parse(body);
            if (obj.data.length) {
                var stream = obj.data[0];
                var found_stream = _.pick(stream, _.keys(stream_template));
                found_stream['filter_id'] = stream.filter && stream.filter.id;

                if (_.isEqual(found_stream, stream_template)) {
                    console.log('reusing stream id', stream.id);
                    cb(stream.endpoint);
                } else {
                    // delete stream so we can recreate it with correct template
                    request.del({
                        url: stream_url + '/' + stream.id,
                        headers: headers
                    }, function (e, r, body) {
                        if (!e && r.statusCode === 200){
                            create_stream();
                        } else {
                            console.log('Error deleting stream ' + stream.id);
                            cb();
                        }
                    });
                }
            } else {
                create_stream();
            }
        } else {
            console.log('Error getting stream', e, 'status', r.statusCode, 'body', body);
            cb();
        }
    });
};

StreamRouter.prototype.stream = function (token) {
    var self = this;

    var listen_to_endpoint = function (app_token, endpoint) {
        var stream = new ADNStream(endpoint);

        var channel_subs_cache = {};
        var in_flight_request_promises = {};

        stream.on('channel_subscription', function (msg) {
            channel_id = msg.data.channel.id;

            // only accept incremental updates for channels
            // we have seen and received a server response for
            Q.when(in_flight_request_promises[channel_id], function () {
                if (channel_subs_cache[channel_id]) {
                    if (msg.meta.deleted) {
                        channel_subs_cache[channel_id] = _.without(channel_subs_cache[channel_id], msg.data.user.id);
                    } else {
                        channel_subs_cache[channel_id].push(msg.data.user.id);
                    }
                }
            });
        });

        var get_users_for_channel = function (channel_id) {
            if (in_flight_request_promises[channel_id]) {
                return in_flight_request_promises[channel_id];
            }

            if (!channel_subs_cache[channel_id]) {
                var deferred = Q.defer();
                in_flight_request_promises[channel_id] = deferred.promise;

                var headers = {
                    authorization: 'Bearer ' + app_token,
                    host: api_host_override
                };

                request.get({
                    url: api_url_base + '/stream/0/channels/' + channel_id + '/subscribers/ids',
                    headers: headers
                }, function (e, r, body) {
                    if (!e && r.statusCode === 200) {
                        var ids = JSON.parse(body).data || [];
                        channel_subs_cache[channel_id] = ids;
                        delete in_flight_request_promises[channel_id];
                        deferred.resolve(ids);
                    } else {
                        if (!e) {
                            e = 'Unexpected response code: ' + r.statusCode + ' ' + r.request.url;
                        }

                        // If there was an error, dump the cache so we go back
                        // and refresh the full list again.
                        delete channel_subs_cache[channel_id];
                        delete in_flight_request_promises[channel_id];
                        deferred.reject(new Error (e));
                    }
                });

                return deferred.promise;
            }

            return channel_subs_cache[channel_id];
        };

        var send_message_to_channel = function (msg, channel_id) {
            Q.when(get_users_for_channel(channel_id), function (user_ids) {
                _.each(user_ids, function (user_id) {
                    self.lightpoll.dispatch(user_id, msg);
                });
            }, function (error) {
                console.log('error here', error);
            });
        };

        stream.on('channel', function (msg) {
            send_message_to_channel(msg, msg.meta.id);
        });

        stream.on('stream_marker', function (msg) {
            // currently we get *all* stream markers
            if (msg.meta.channel_id) {
                send_message_to_channel(msg, msg.meta.channel_id);
            }
        });

        stream.on('message', function (msg) {
            send_message_to_channel(msg, msg.data.channel_id);
        });

        stream.on('error', function (error) {
            console.error('Stream error, reconnecting:', error);
            setTimeout(function () {
                stream.process(true);
            }, 2000);
        });

        stream.on('end', function () {
            console.error('Stream ended, reconnecting.');
            setTimeout(function () {
                stream.process(true);
            }, 2000);
        });

        stream.process(true);
    };

    self.get_or_create_stream(token, function (endpoint) {
        listen_to_endpoint(token, endpoint);
    });
};

module.exports.create_router = function (app, lightpoll) {
    return new StreamRouter(app, lightpoll);
};
<|endoftext|>


<commit_before><commit_msg>handle case of parameters after<commit_after><|endoftext|>


<commit_before>/**	Fruit.js
  *
  *	Documentation generator from Markdown files
  *
  *	Dependencies:
  *		- markdown-js
  *		- RSVP
  *		- LESS
  *
  **/

var Document = require('./src/Document.js'),
	$ = require('./src/Utils.js'),
	path = require('path'),
	fs = require('fs');

function FruitJS (options, relativePath) {
	this.options = options;
	this.path = relativePath;
	this.document = new Document(options, relativePath);

	var prevDir = process.cwd(),

		doc = this.document,
		addTreeFn = function (arr, fn) {
				for(var a in arr) {
					addItemFn(arr[a], fn);
				}
			},
		addItemFn = function (itm, fn) {
				if(!fs.existsSync(itm)) {
					console.error('File not found.', itm);
					process.exit();
				} else if(fs.statSync(itm).isDirectory()) {
					var sub = fs.readdirSync(itm);
					for(var s in sub)
						sub[s] = itm + path.sep + sub[s];
					addTreeFn(sub,fn);
				} else {
					fn(itm);
				}
			};

	process.chdir(path.dirname(relativePath));

	addTreeFn(options.css, doc.addCSS.bind(doc));
	addTreeFn(options.less, doc.addLESS.bind(doc));
	addTreeFn(options.js, doc.addJS.bind(doc));
	addTreeFn(options.images, doc.addImage.bind(doc));
	addTreeFn(options.assets, doc.addAsset.bind(doc));

	if(options.imageTitle) {
		doc.addImage(options.imageTitle);
		doc.setImageTitle(path.basename(options.imageTitle));
	}

	if(options.noExtraction) {
		doc.disableExtraction();
	}

	if(options.preMenu) {
		if(options.preMenu instanceof Array) {
			for(var p in options.preMenu) {
				doc.addBeforeMenu(options.preMenu[p]);
			}
		} else {
			doc.addBeforeMenu(options.preMenu);
		}
	}
	if(options.postMenu) {
		if(options.postMenu instanceof Array) {
			for(var p in options.postMenu) {
				doc.addAfterMenu(options.postMenu[p]);
			}
		} else {
			doc.addAfterMenu(options.postMenu);
		}
	}

	for(var p in options.pages)
		doc.addPage($.GetRelativePath(this.path, options.pages[p]));

	process.chdir(prevDir);
}

FruitJS.prototype.render = function(folder) {
	return this.document.render(folder, this.options.singlePage);
}

module.exports = FruitJS;<commit_msg>Don't bail on file not found error<commit_after>/**	Fruit.js
  *
  *	Documentation generator from Markdown files
  *
  *	Dependencies:
  *		- markdown-js
  *		- RSVP
  *		- LESS
  *
  **/

var Document = require('./src/Document.js'),
	$ = require('./src/Utils.js'),
	path = require('path'),
	fs = require('fs');

function FruitJS (options, relativePath) {
	this.options = options;
	this.path = relativePath;
	this.document = new Document(options, relativePath);

	var prevDir = process.cwd(),

		doc = this.document,
		addTreeFn = function (arr, fn) {
				for(var a in arr) {
					addItemFn(arr[a], fn);
				}
			},
		addItemFn = function (itm, fn) {
				if(!fs.existsSync(itm)) {
					console.error('File not found.', itm);
					throw new Error('Unable to find file '+item);
				} else if(fs.statSync(itm).isDirectory()) {
					var sub = fs.readdirSync(itm);
					for(var s in sub)
						sub[s] = itm + path.sep + sub[s];
					addTreeFn(sub,fn);
				} else {
					fn(itm);
				}
			};

	process.chdir(path.dirname(relativePath));

	addTreeFn(options.css, doc.addCSS.bind(doc));
	addTreeFn(options.less, doc.addLESS.bind(doc));
	addTreeFn(options.js, doc.addJS.bind(doc));
	addTreeFn(options.images, doc.addImage.bind(doc));
	addTreeFn(options.assets, doc.addAsset.bind(doc));

	if(options.imageTitle) {
		doc.addImage(options.imageTitle);
		doc.setImageTitle(path.basename(options.imageTitle));
	}

	if(options.noExtraction) {
		doc.disableExtraction();
	}

	if(options.preMenu) {
		if(options.preMenu instanceof Array) {
			for(var p in options.preMenu) {
				doc.addBeforeMenu(options.preMenu[p]);
			}
		} else {
			doc.addBeforeMenu(options.preMenu);
		}
	}
	if(options.postMenu) {
		if(options.postMenu instanceof Array) {
			for(var p in options.postMenu) {
				doc.addAfterMenu(options.postMenu[p]);
			}
		} else {
			doc.addAfterMenu(options.postMenu);
		}
	}

	for(var p in options.pages)
		doc.addPage($.GetRelativePath(this.path, options.pages[p]));

	process.chdir(prevDir);
}

FruitJS.prototype.render = function(folder) {
	return this.document.render(folder, this.options.singlePage);
}

module.exports = FruitJS;<|endoftext|>


<commit_before>$(function() {
	$('.dropdown-toggle').dropdown();
	$('.dropdown input, .dropdown label').click(function(e) {
		e.stopPropagation();
	});
});

// FOR TESTING PURPOSES<commit_msg>js stuff<commit_after>$(function() {
	$('.dropdown-toggle').dropdown();
	$('.dropdown input, .dropdown label').click(function(e) {
		e.stopPropagation();
	});
});

// FOR TESTING PURPOSES
$( document ).ready(function() {
	console.log( "ready!" );
});<|endoftext|>


<commit_before>

function getCard(user, cardID){

	if(user == 'a'){
		return '/html/resources/users/a/' + cardID + '.html'
	}else if(user == 'b'){
		return '/html/resources/users/b/' + cardID + '.html'
	}else{
		alert("ERROR");
	}
}<commit_msg>Change the javascript here to the test js from Test.html<commit_after>/**
Gets the resource url from the database based on this page's url
*/
function getResourceURL(){}
	var hash = window.location.hash.substring(1);

	var resourceURL = "";

	console.log(hash);

	$.post('../../php/getResourceURL.php', {'hash': hash}, function(data){
		resourceURL = data;
	});
}<|endoftext|>