<commit_before>// Github.js 0.7.0
// (c) 2012 Michael Aufreiter, Development Seed
// Github.js is freely distributable under the MIT license.
// For all details and documentation:
// http://substance.io/michael/github

(function() {
  var Github;
  var API_URL = 'https://api.github.com';

  Github = window.Github = function(options) {

    // Util
    // =======

    function _request(method, path, data, cb, raw) {
      var xhr = new XMLHttpRequest();
      if (!raw) {xhr.dataType = "json"}
      xhr.open(method, API_URL + path);
      xhr.onreadystatechange = function () {
        if (this.readyState == 4) {
          if (this.status >= 200 && this.status < 300 || this.status === 304) {
            cb(null, raw ? this.responseText : JSON.parse(this.responseText));
          } else {
            cb(this.status);
          }
        }
      }
      xhr.setRequestHeader('Accept','application/vnd.github.raw');
      xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
      if (
         (options.auth == 'oauth' && options.token) ||
         (options.auth == 'basic' && options.username && options.password)
         ) {
           xhr.setRequestHeader('Authorization',options.auth == 'oauth'
             ? 'token '+ options.token
             : 'Basic ' + Base64.encode(options.username + ':' + options.password)
           );
         }
      data ? xhr.send(JSON.stringify(data)) : xhr.send();
    }

    // User API
    // =======

    Github.User = function() {
      this.repos = function(cb) {
        _request("GET", "/user/repos?type=all&per_page=1000&sort=updated", null, function(err, res) {
          cb(err, res);
        });
      };

      // List user organizations
      // -------

      this.orgs = function(cb) {
        _request("GET", "/user/orgs", null, function(err, res) {
          cb(err, res);
        });
      };

      // List authenticated user's gists
      // -------

      this.gists = function(cb) {
        _request("GET", "/gists", null, function(err, res) {
          cb(err,res);
        });
      };

      // Show user information
      // -------

      this.show = function(username, cb) {
        _request("GET", "/users/"+username, null, function(err, res) {
          cb(err, res);
        });
      }

      // List user repositories
      // -------

      this.userRepos = function(username, cb) {
        _request("GET", "/users/"+username+"/repos?type=all&per_page=1000&sort=updated", null, function(err, res) {
          cb(err, res);
        });
      };

      // List a user's gists
      // -------

      this.userGists = function(username, cb) {
        _request("GET", "/users/"+username+"/gists", null, function(err, res) {
          cb(err,res);
        });
      };

      // List organization repositories
      // -------

      this.orgRepos = function(orgname, cb) {
        _request("GET", "/orgs/"+orgname+"/repos?type=all&per_page=1000&sort=updated&direction=desc", null, function(err, res) {
          cb(err, res);
        });
      };
    };

    // Repository API
    // =======

    Github.Repository = function(options) {
      var repo = options.name;
      var user = options.user;

      var that = this;
      var repoPath = "/repos/" + user + "/" + repo;

      var currentTree = {
        "branch": null,
        "sha": null
      };

      // Uses the cache if branch has not been changed
      // -------

      function updateTree(branch, cb) {
        if (branch === currentTree.branch && currentTree.sha) return cb(null, currentTree.sha);
        that.getRef(branch, function(err, sha) {
          currentTree.branch = branch;
          currentTree.sha = sha;
          cb(err, sha);
        });
      }

      // Get a particular reference
      // -------

      this.getRef = function(ref, cb) {
        _request("GET", repoPath + "/git/refs/heads/" + ref, null, function(err, res) {
          if (err) return cb(err);
          cb(null, res.object.sha);
        });
      };

      // List all branches of a repository
      // -------

      this.listBranches = function(cb) {
        _request("GET", repoPath + "/git/refs/heads", null, function(err, heads) {
          if (err) return cb(err);
          cb(null, _.map(heads, function(head) { return _.last(head.ref.split('/')); }));
        });
      };

      // Retrieve the contents of a blob
      // -------

      this.getBlob = function(sha, cb) {
        _request("GET", repoPath + "/git/blobs/" + sha, null, cb, 'raw');
      };

      // For a given file path, get the corresponding sha (blob for files, tree for dirs)
      // -------

      this.getSha = function(branch, path, cb) {
        // Just use head if path is empty
        if (path === "") return that.getRef(branch, cb);
        that.getTree(branch+"?recursive=true", function(err, tree) {
          var file = _.select(tree, function(file) {
            return file.path === path;
          })[0];
          cb(null, file ? file.sha : null);
        });
      };

      // Retrieve the tree a commit points to
      // -------

      this.getTree = function(tree, cb) {
        _request("GET", repoPath + "/git/trees/"+tree, null, function(err, res) {
          if (err) return cb(err);
          cb(null, res.tree);
        });
      };

      // Post a new blob object, getting a blob SHA back
      // -------

      this.postBlob = function(content, cb) {
        if (typeof(content) === "string") {
          content = {
            "content": content,
            "encoding": "utf-8"
          };
        }

        _request("POST", repoPath + "/git/blobs", content, function(err, res) {
          if (err) return cb(err);
          cb(null, res.sha);
        });
      };

      // Update an existing tree adding a new blob object getting a tree SHA back
      // -------

      this.updateTree = function(baseTree, path, blob, cb) {
        var data = {
          "base_tree": baseTree,
          "tree": [
            {
              "path": path,
              "mode": "100644",
              "type": "blob",
              "sha": blob
            }
          ]
        };
        _request("POST", repoPath + "/git/trees", data, function(err, res) {
          if (err) return cb(err);
          cb(null, res.sha);
        });
      };

      // Post a new tree object having a file path pointer replaced
      // with a new blob SHA getting a tree SHA back
      // -------

      this.postTree = function(tree, cb) {
        _request("POST", repoPath + "/git/trees", { "tree": tree }, function(err, res) {
          if (err) return cb(err);
          cb(null, res.sha);
        });
      };

      // Create a new commit object with the current commit SHA as the parent
      // and the new tree SHA, getting a commit SHA back
      // -------

      this.commit = function(parent, tree, message, cb) {
        var data = {
          "message": message,
          "author": {
            "name": options.username
          },
          "parents": [
            parent
          ],
          "tree": tree
        };

        _request("POST", repoPath + "/git/commits", data, function(err, res) {
          currentTree.sha = res.sha; // update latest commit
          if (err) return cb(err);
          cb(null, res.sha);
        });
      };

      // Update the reference of your head to point to the new commit SHA
      // -------

      this.updateHead = function(head, commit, cb) {
        _request("PATCH", repoPath + "/git/refs/heads/" + head, { "sha": commit }, function(err, res) {
          cb(err);
        });
      };

      // Show repository information
      // -------

      this.show = function(cb) {
        _request("GET", repoPath, null, function(err, info) {
          cb(null, info);
        });
      };

      // Read file at given path
      // -------

      this.read = function(branch, path, cb) {
        that.getSha(branch, path, function(err, sha) {
          if (!sha) return cb("not found", null);
          that.getBlob(sha, cb);
        });
      };

      // Remove a file from the tree
      // -------

      this.remove = function(branch, path, cb) {
        updateTree(branch, function(err, latestCommit) {
          that.getTree(latestCommit+"?recursive=true", function(err, tree) {
            // Update Tree
            var newTree = _.reject(tree, function(ref) { return ref.path === path });
            _.each(newTree, function(ref) {
              if (ref.type === "tree") delete ref.sha;
            });

            that.postTree(newTree, function(err, rootTree) {
              that.commit(latestCommit, rootTree, 'Deleted '+path , function(err, commit) {
                that.updateHead(branch, commit, function(err) {
                  cb(err);
                });
              });
            });
          });
        });
      };

      // Move a file to a new location
      // -------

      this.move = function(branch, path, newPath, cb) {
        updateTree(branch, function(err, latestCommit) {
          that.getTree(latestCommit+"?recursive=true", function(err, tree) {
            // Update Tree
            _.each(tree, function(ref) {
              if (ref.path === path) ref.path = newPath;
              if (ref.type === "tree") delete ref.sha;
            });

            that.postTree(tree, function(err, rootTree) {
              that.commit(latestCommit, rootTree, 'Deleted '+path , function(err, commit) {
                that.updateHead(branch, commit, function(err) {
                  cb(err);
                });
              });
            });
          });
        });
      };

      // Write file contents to a given branch and path
      // -------

      this.write = function(branch, path, content, message, cb) {
        updateTree(branch, function(err, latestCommit) {
          that.postBlob(content, function(err, blob) {
            that.updateTree(latestCommit, path, blob, function(err, tree) {
              that.commit(latestCommit, tree, message, function(err, commit) {
                that.updateHead(branch, commit, cb);
              });
            });
          });
        });
      };
    };

    // Gists API
    // =======

    Github.Gist = function(options) {
      var id = options.id;
      var that = this;
      var gistPath = "/gists/"+id;

      // Read the gist
      // --------

      this.read = function(cb) {
        _request("GET", gistPath, null, function(err, gist) {
          cb(err, gist);
        });
      };

      // Delete the gist
      // --------

      this.delete = function(cb) {
        _request("DELETE", gistPath, null, function(err,res) {
          cb(err,res);
        });
      };

      // Fork a gist
      // --------

      this.fork = function(cb) {
        _request("POST", gistPath+"/fork", null, function(err,res) {
          cb(err,res);
        });
      };

      // Update a gist with the new stuff
      // --------

      this.update = function(options, cb) {
        _request("PATCH", gistPath, options, function(err,res) {
          cb(err,res);
        });
      };
    };

    // Top Level API
    // -------

    this.getRepo = function(user, repo) {
      return new Github.Repository({user: user, name: repo});
    };

    this.getUser = function() {
      return new Github.User();
    };

    this.getGist = function(id) {
      return new Github.Gist({id: id});
    };
  };
}).call(this);<commit_msg>Output true if there is no responseText<commit_after>// Github.js 0.7.0
// (c) 2012 Michael Aufreiter, Development Seed
// Github.js is freely distributable under the MIT license.
// For all details and documentation:
// http://substance.io/michael/github

(function() {
  var Github;
  var API_URL = 'https://api.github.com';

  Github = window.Github = function(options) {

    // Util
    // =======

    function _request(method, path, data, cb, raw) {
      var xhr = new XMLHttpRequest();
      if (!raw) {xhr.dataType = "json"}
      xhr.open(method, API_URL + path);
      xhr.onreadystatechange = function () {
        if (this.readyState == 4) {
          if (this.status >= 200 && this.status < 300 || this.status === 304) {
            cb(null, raw ? this.responseText : this.responseText ? JSON.parse(this.responseText) : true);
          } else {
            cb(this.status);
          }
        }
      }
      xhr.setRequestHeader('Accept','application/vnd.github.raw');
      xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
      if (
         (options.auth == 'oauth' && options.token) ||
         (options.auth == 'basic' && options.username && options.password)
         ) {
           xhr.setRequestHeader('Authorization',options.auth == 'oauth'
             ? 'token '+ options.token
             : 'Basic ' + Base64.encode(options.username + ':' + options.password)
           );
         }
      data ? xhr.send(JSON.stringify(data)) : xhr.send();
    }

    // User API
    // =======

    Github.User = function() {
      this.repos = function(cb) {
        _request("GET", "/user/repos?type=all&per_page=1000&sort=updated", null, function(err, res) {
          cb(err, res);
        });
      };

      // List user organizations
      // -------

      this.orgs = function(cb) {
        _request("GET", "/user/orgs", null, function(err, res) {
          cb(err, res);
        });
      };

      // List authenticated user's gists
      // -------

      this.gists = function(cb) {
        _request("GET", "/gists", null, function(err, res) {
          cb(err,res);
        });
      };

      // Show user information
      // -------

      this.show = function(username, cb) {
        _request("GET", "/users/"+username, null, function(err, res) {
          cb(err, res);
        });
      }

      // List user repositories
      // -------

      this.userRepos = function(username, cb) {
        _request("GET", "/users/"+username+"/repos?type=all&per_page=1000&sort=updated", null, function(err, res) {
          cb(err, res);
        });
      };

      // List a user's gists
      // -------

      this.userGists = function(username, cb) {
        _request("GET", "/users/"+username+"/gists", null, function(err, res) {
          cb(err,res);
        });
      };

      // List organization repositories
      // -------

      this.orgRepos = function(orgname, cb) {
        _request("GET", "/orgs/"+orgname+"/repos?type=all&per_page=1000&sort=updated&direction=desc", null, function(err, res) {
          cb(err, res);
        });
      };
    };

    // Repository API
    // =======

    Github.Repository = function(options) {
      var repo = options.name;
      var user = options.user;

      var that = this;
      var repoPath = "/repos/" + user + "/" + repo;

      var currentTree = {
        "branch": null,
        "sha": null
      };

      // Uses the cache if branch has not been changed
      // -------

      function updateTree(branch, cb) {
        if (branch === currentTree.branch && currentTree.sha) return cb(null, currentTree.sha);
        that.getRef(branch, function(err, sha) {
          currentTree.branch = branch;
          currentTree.sha = sha;
          cb(err, sha);
        });
      }

      // Get a particular reference
      // -------

      this.getRef = function(ref, cb) {
        _request("GET", repoPath + "/git/refs/heads/" + ref, null, function(err, res) {
          if (err) return cb(err);
          cb(null, res.object.sha);
        });
      };

      // List all branches of a repository
      // -------

      this.listBranches = function(cb) {
        _request("GET", repoPath + "/git/refs/heads", null, function(err, heads) {
          if (err) return cb(err);
          cb(null, _.map(heads, function(head) { return _.last(head.ref.split('/')); }));
        });
      };

      // Retrieve the contents of a blob
      // -------

      this.getBlob = function(sha, cb) {
        _request("GET", repoPath + "/git/blobs/" + sha, null, cb, 'raw');
      };

      // For a given file path, get the corresponding sha (blob for files, tree for dirs)
      // -------

      this.getSha = function(branch, path, cb) {
        // Just use head if path is empty
        if (path === "") return that.getRef(branch, cb);
        that.getTree(branch+"?recursive=true", function(err, tree) {
          var file = _.select(tree, function(file) {
            return file.path === path;
          })[0];
          cb(null, file ? file.sha : null);
        });
      };

      // Retrieve the tree a commit points to
      // -------

      this.getTree = function(tree, cb) {
        _request("GET", repoPath + "/git/trees/"+tree, null, function(err, res) {
          if (err) return cb(err);
          cb(null, res.tree);
        });
      };

      // Post a new blob object, getting a blob SHA back
      // -------

      this.postBlob = function(content, cb) {
        if (typeof(content) === "string") {
          content = {
            "content": content,
            "encoding": "utf-8"
          };
        }

        _request("POST", repoPath + "/git/blobs", content, function(err, res) {
          if (err) return cb(err);
          cb(null, res.sha);
        });
      };

      // Update an existing tree adding a new blob object getting a tree SHA back
      // -------

      this.updateTree = function(baseTree, path, blob, cb) {
        var data = {
          "base_tree": baseTree,
          "tree": [
            {
              "path": path,
              "mode": "100644",
              "type": "blob",
              "sha": blob
            }
          ]
        };
        _request("POST", repoPath + "/git/trees", data, function(err, res) {
          if (err) return cb(err);
          cb(null, res.sha);
        });
      };

      // Post a new tree object having a file path pointer replaced
      // with a new blob SHA getting a tree SHA back
      // -------

      this.postTree = function(tree, cb) {
        _request("POST", repoPath + "/git/trees", { "tree": tree }, function(err, res) {
          if (err) return cb(err);
          cb(null, res.sha);
        });
      };

      // Create a new commit object with the current commit SHA as the parent
      // and the new tree SHA, getting a commit SHA back
      // -------

      this.commit = function(parent, tree, message, cb) {
        var data = {
          "message": message,
          "author": {
            "name": options.username
          },
          "parents": [
            parent
          ],
          "tree": tree
        };

        _request("POST", repoPath + "/git/commits", data, function(err, res) {
          currentTree.sha = res.sha; // update latest commit
          if (err) return cb(err);
          cb(null, res.sha);
        });
      };

      // Update the reference of your head to point to the new commit SHA
      // -------

      this.updateHead = function(head, commit, cb) {
        _request("PATCH", repoPath + "/git/refs/heads/" + head, { "sha": commit }, function(err, res) {
          cb(err);
        });
      };

      // Show repository information
      // -------

      this.show = function(cb) {
        _request("GET", repoPath, null, function(err, info) {
          cb(null, info);
        });
      };

      // Read file at given path
      // -------

      this.read = function(branch, path, cb) {
        that.getSha(branch, path, function(err, sha) {
          if (!sha) return cb("not found", null);
          that.getBlob(sha, cb);
        });
      };

      // Remove a file from the tree
      // -------

      this.remove = function(branch, path, cb) {
        updateTree(branch, function(err, latestCommit) {
          that.getTree(latestCommit+"?recursive=true", function(err, tree) {
            // Update Tree
            var newTree = _.reject(tree, function(ref) { return ref.path === path });
            _.each(newTree, function(ref) {
              if (ref.type === "tree") delete ref.sha;
            });

            that.postTree(newTree, function(err, rootTree) {
              that.commit(latestCommit, rootTree, 'Deleted '+path , function(err, commit) {
                that.updateHead(branch, commit, function(err) {
                  cb(err);
                });
              });
            });
          });
        });
      };

      // Move a file to a new location
      // -------

      this.move = function(branch, path, newPath, cb) {
        updateTree(branch, function(err, latestCommit) {
          that.getTree(latestCommit+"?recursive=true", function(err, tree) {
            // Update Tree
            _.each(tree, function(ref) {
              if (ref.path === path) ref.path = newPath;
              if (ref.type === "tree") delete ref.sha;
            });

            that.postTree(tree, function(err, rootTree) {
              that.commit(latestCommit, rootTree, 'Deleted '+path , function(err, commit) {
                that.updateHead(branch, commit, function(err) {
                  cb(err);
                });
              });
            });
          });
        });
      };

      // Write file contents to a given branch and path
      // -------

      this.write = function(branch, path, content, message, cb) {
        updateTree(branch, function(err, latestCommit) {
          that.postBlob(content, function(err, blob) {
            that.updateTree(latestCommit, path, blob, function(err, tree) {
              that.commit(latestCommit, tree, message, function(err, commit) {
                that.updateHead(branch, commit, cb);
              });
            });
          });
        });
      };
    };

    // Gists API
    // =======

    Github.Gist = function(options) {
      var id = options.id;
      var that = this;
      var gistPath = "/gists/"+id;

      // Read the gist
      // --------

      this.read = function(cb) {
        _request("GET", gistPath, null, function(err, gist) {
          cb(err, gist);
        });
      };

      // Delete the gist
      // --------

      this.delete = function(cb) {
        _request("DELETE", gistPath, null, function(err,res) {
          cb(err,res);
        });
      };

      // Fork a gist
      // --------

      this.fork = function(cb) {
        _request("POST", gistPath+"/fork", null, function(err,res) {
          cb(err,res);
        });
      };

      // Update a gist with the new stuff
      // --------

      this.update = function(options, cb) {
        _request("PATCH", gistPath, options, function(err,res) {
          cb(err,res);
        });
      };
    };

    // Top Level API
    // -------

    this.getRepo = function(user, repo) {
      return new Github.Repository({user: user, name: repo});
    };

    this.getUser = function() {
      return new Github.User();
    };

    this.getGist = function(id) {
      return new Github.Gist({id: id});
    };
  };
}).call(this);<|endoftext|>


<commit_before>/*
 * new Vizard.Filter.SSI({
 *   blacklist: [/etracker.include$/],
 *   cache: true,
 *   location: vizard.location,
 *   writeback: function(url, contents) {
 *     // ...
 *   }
 * }).bind( vizard );
 */
(function(Vizard) {

	var includeExpr  = /<!--# include .*?-->/g
	  , extroComment = '<!--# end-of include="%s" -->'
	  , dropQuestion = 'Drop dead reference "%s"?',

	$ = Vizard.jQuery,

	defaults = {
		blacklist: false,
		cache: true,
		location: window.location,
		writeback: false
	};

	function Absolute(agent, location) {
		var prefix = location.protocol + '//' + location.host
		  , href   = location.href
		  , self   = this;

		self.absolute = function(url) {
			return url.substr(0, 1) == '/' ?
				prefix + url :
				href.replace(/[^\/]*$/, url);
		};

		self.agent = agent;

		return self;
	}
	$.extend(Absolute.prototype, {
		get: function(url) {
			return this.agent.get( this.absolute(url) );
		},
		set: function(url, contents) {
			return this.agent.set( this.absolute(url), contents );
		}
	});
	function Blacklist(agent, expressions) {
		var blacklist = this;

		blacklist.expressions = expressions;
		blacklist.agent       = agent;

		return blacklist;
	}
	$.extend(Blacklist.prototype, {
		rejected: function(url) {
			var result = false;

			$.each( this.expressions, function() {
				return !( result = this.test(url) );
			});

			return result;
		},
		get: function(url) {
			return this.rejected(url) ? null : this.agent.get( url );
		},
		set: function(url, contents) {
			return this.rejected(url) ? null : this.agent.set( url, contents );
		}
	});
	function Cache(agent) {
		var cache = this;

		cache.storage = {};
		cache.agent   = agent;

		return cache;
	}
	$.extend(Cache.prototype, {
		get: function(url) {
			var contents = this.storage[url];
			if ( contents === undefined ) {
				contents = this.storage[url] = this.agent.get(url);
			}
			return contents;
		},
		set: function(url, contents) {
			if ( this.storage[url] === contents ) {
				return contents;
			} else {
				return this.storage[url] = this.agent.set(url, contents);
			}
		}
	});
	function Agent(agent) {
		var self = this;
		self.agent = agent;
		return self;
	}
	$.extend(Agent.prototype, {
		get: function(url) {
			var value = null;

			$.ajax( url, {
				async: false, dataType: 'text',
				success: function(text) { value = text; }
			});

			return value;
		},
		set: function(url, contents) {
			this.agent.set(url, contents);
		}
	});

	function dropped(url) {
		return confirm( dropQuestion.replace('%s', url) );
	}

	function SSI( opts ) {
		var agent, filter;

		opts = $.extend( defaults, opts );

		agent = new Agent(this);
		if (opts.cache) { agent = new Cache(agent); }
		if (opts.blacklist) { agent = new Blacklist(agent, opts.blacklist); }
		agent = new Absolute(agent, opts.location);

		filter = {
			bind: function(vizard) {
				vizard.inputFilter.unshift(includeSSI);
				vizard.outputFilter.push(excludeSSI);
			},
			includeSSi: includeSSI,
			excludeSSI: excludeSSI
		};

		if ( typeof(opts.writeback) == 'function' ) {
			filter.set = opts.writeback;
		} else {
			function nop() {};
		}

		function includeSSI( source ) {
			// RADAR maybe this should split and resolve async and wait until
			// all includes are loaded, see jQuery.deferred...
			return source.replace(includeExpr, function(intro) {
				var url      = intro.match(/="(.*)"/)[1]
				  , contents = agent.get(url);

				if ( contents !== null ) {
					return intro + contents + extroComment.replace('%s', url);
				} else {
					return dropped(url) ? '' : intro;
				}
			});
		}
		function excludeSSI( source ) {
			var includes = {}, urls = []
			  , start, stop;

			// url extration
			$.each( source.match(includeExpr), function() {
				var include  = this.toString()
				  , url      = include.match(/="([^"]+)"/)[1];

				includes[url] = include;
			});

			// data mangling and writeBack
			$.each( includes, function( url, intro ) {
				var extro         = extroComment.replace('%s', url)
				  , lengthOfExtro = extro.length
				  , lengthOfIntro = intro.length
				  , contents;

				stop = source.indexOf(extro);
				while ( stop > -1 ) {
					start = source.lastIndexOf(intro, stop);
					if ( start < 0 ) {
						throw('Vizard.Filter.SSI: Extro without Intro!');
					}

					// extract contents
					contents = source.substring(start + lengthOfIntro, stop);

					// remove contents and extro
					source = ''.concat(
						source.substr(0, start + lengthOfIntro),
						source.substr(stop + lengthOfExtro)
					);

					stop = source.indexOf(extro);
				}

				agent.set(url, contents);
			});

			return source;
		}

		return filter;
	};

	Vizard.Filter.SSI = SSI;

})(Vizard);
<commit_msg>Removed unnecessary semicolons.<commit_after>/*
 * new Vizard.Filter.SSI({
 *   blacklist: [/etracker.include$/],
 *   cache: true,
 *   location: vizard.location,
 *   writeback: function(url, contents) {
 *     // ...
 *   }
 * }).bind( vizard );
 */
(function(Vizard) {

	var includeExpr  = /<!--# include .*?-->/g
	  , extroComment = '<!--# end-of include="%s" -->'
	  , dropQuestion = 'Drop dead reference "%s"?',

	$ = Vizard.jQuery,

	defaults = {
		blacklist: false,
		cache: true,
		location: window.location,
		writeback: false
	};

	function Absolute(agent, location) {
		var prefix = location.protocol + '//' + location.host
		  , href   = location.href
		  , self   = this;

		self.absolute = function(url) {
			return url.substr(0, 1) == '/' ?
				prefix + url :
				href.replace(/[^\/]*$/, url);
		};

		self.agent = agent;

		return self;
	}
	$.extend(Absolute.prototype, {
		get: function(url) {
			return this.agent.get( this.absolute(url) );
		},
		set: function(url, contents) {
			return this.agent.set( this.absolute(url), contents );
		}
	});
	function Blacklist(agent, expressions) {
		var blacklist = this;

		blacklist.expressions = expressions;
		blacklist.agent       = agent;

		return blacklist;
	}
	$.extend(Blacklist.prototype, {
		rejected: function(url) {
			var result = false;

			$.each( this.expressions, function() {
				return !( result = this.test(url) );
			});

			return result;
		},
		get: function(url) {
			return this.rejected(url) ? null : this.agent.get( url );
		},
		set: function(url, contents) {
			return this.rejected(url) ? null : this.agent.set( url, contents );
		}
	});
	function Cache(agent) {
		var cache = this;

		cache.storage = {};
		cache.agent   = agent;

		return cache;
	}
	$.extend(Cache.prototype, {
		get: function(url) {
			var contents = this.storage[url];
			if ( contents === undefined ) {
				contents = this.storage[url] = this.agent.get(url);
			}
			return contents;
		},
		set: function(url, contents) {
			if ( this.storage[url] === contents ) {
				return contents;
			} else {
				return this.storage[url] = this.agent.set(url, contents);
			}
		}
	});
	function Agent(agent) {
		var self = this;
		self.agent = agent;
		return self;
	}
	$.extend(Agent.prototype, {
		get: function(url) {
			var value = null;

			$.ajax( url, {
				async: false, dataType: 'text',
				success: function(text) { value = text; }
			});

			return value;
		},
		set: function(url, contents) {
			this.agent.set(url, contents);
		}
	});

	function dropped(url) {
		return confirm( dropQuestion.replace('%s', url) );
	}

	function SSI( opts ) {
		var agent, filter;

		opts = $.extend( defaults, opts );

		agent = new Agent(this);
		if (opts.cache) { agent = new Cache(agent); }
		if (opts.blacklist) { agent = new Blacklist(agent, opts.blacklist); }
		agent = new Absolute(agent, opts.location);

		filter = {
			bind: function(vizard) {
				vizard.inputFilter.unshift(includeSSI);
				vizard.outputFilter.push(excludeSSI);
			},
			includeSSi: includeSSI,
			excludeSSI: excludeSSI
		};

		if ( typeof(opts.writeback) == 'function' ) {
			filter.set = opts.writeback;
		} else {
			function nop() {}
		}

		function includeSSI( source ) {
			// RADAR maybe this should split and resolve async and wait until
			// all includes are loaded, see jQuery.deferred...
			return source.replace(includeExpr, function(intro) {
				var url      = intro.match(/="(.*)"/)[1]
				  , contents = agent.get(url);

				if ( contents !== null ) {
					return intro + contents + extroComment.replace('%s', url);
				} else {
					return dropped(url) ? '' : intro;
				}
			});
		}
		function excludeSSI( source ) {
			var includes = {}, urls = []
			  , start, stop;

			// url extration
			$.each( source.match(includeExpr), function() {
				var include  = this.toString()
				  , url      = include.match(/="([^"]+)"/)[1];

				includes[url] = include;
			});

			// data mangling and writeBack
			$.each( includes, function( url, intro ) {
				var extro         = extroComment.replace('%s', url)
				  , lengthOfExtro = extro.length
				  , lengthOfIntro = intro.length
				  , contents;

				stop = source.indexOf(extro);
				while ( stop > -1 ) {
					start = source.lastIndexOf(intro, stop);
					if ( start < 0 ) {
						throw('Vizard.Filter.SSI: Extro without Intro!');
					}

					// extract contents
					contents = source.substring(start + lengthOfIntro, stop);

					// remove contents and extro
					source = ''.concat(
						source.substr(0, start + lengthOfIntro),
						source.substr(stop + lengthOfExtro)
					);

					stop = source.indexOf(extro);
				}

				agent.set(url, contents);
			});

			return source;
		}

		return filter;
	}

	Vizard.Filter.SSI = SSI;

})(Vizard);
<|endoftext|>


<commit_before>// Github.js 0.8.0
// (c) 2013 Michael Aufreiter, Development Seed
// Github.js is freely distributable under the MIT license.
// For all details and documentation:
// http://substance.io/michael/github

(function() {

  // Initial Setup
  // -------------

  var XMLHttpRequest, Base64, _;
  if (typeof exports !== 'undefined') {
      XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest;
      _ = require('underscore');
      Base64 = require('./lib/base64.js');
  }else{
      _ = window._;
      Base64 = window.Base64;
  }
  //prefer native XMLHttpRequest always
  if (typeof window !== 'undefined' && typeof window.XMLHttpRequest !== 'undefined'){
      XMLHttpRequest = window.XMLHttpRequest;
  }

  var API_URL = 'https://api.github.com';

  var Github = function(options) {

    // HTTP Request Abstraction
    // =======
    //
    // I'm not proud of this and neither should you be if you were responsible for the XMLHttpRequest spec.

    function _request(method, path, data, cb, raw) {
      function getURL() {
        var url = API_URL + path;
        return url + ((/\?/).test(url) ? "&" : "?") + (new Date()).getTime();
      }

      var xhr = new XMLHttpRequest();
      if (!raw) {xhr.dataType = "json";}

      xhr.open(method, getURL());
      xhr.onreadystatechange = function () {
        if (this.readyState == 4) {
          if (this.status >= 200 && this.status < 300 || this.status === 304) {
            cb(null, raw ? this.responseText : this.responseText ? JSON.parse(this.responseText) : true);
          } else {
            cb({request: this, error: this.status});
          }
        }
      };
      xhr.setRequestHeader('Accept','application/vnd.github.raw');
      xhr.setRequestHeader('Content-Type','application/json');
      if (
         (options.auth == 'oauth' && options.token) ||
         (options.auth == 'basic' && options.username && options.password)
         ) {
           xhr.setRequestHeader('Authorization',options.auth == 'oauth'
             ? 'token '+ options.token
             : 'Basic ' + Base64.encode(options.username + ':' + options.password)
           );
         }
      data ? xhr.send(JSON.stringify(data)) : xhr.send();
    }

    // User API
    // =======

    Github.User = function() {
      this.repos = function(cb) {
        _request("GET", "/user/repos?type=all&per_page=1000&sort=updated", null, function(err, res) {
          cb(err, res);
        });
      };

      // List user organizations
      // -------

      this.orgs = function(cb) {
        _request("GET", "/user/orgs", null, function(err, res) {
          cb(err, res);
        });
      };

      // List authenticated user's gists
      // -------

      this.gists = function(cb) {
        _request("GET", "/gists", null, function(err, res) {
          cb(err,res);
        });
      };

      // Show user information
      // -------

      this.show = function(username, cb) {
        var command = username ? "/users/"+username : "/user";

        _request("GET", command, null, function(err, res) {
          cb(err, res);
        });
      };

      // List user repositories
      // -------

      this.userRepos = function(username, cb) {
        _request("GET", "/users/"+username+"/repos?type=all&per_page=1000&sort=updated", null, function(err, res) {
          cb(err, res);
        });
      };

      // List a user's gists
      // -------

      this.userGists = function(username, cb) {
        _request("GET", "/users/"+username+"/gists", null, function(err, res) {
          cb(err,res);
        });
      };

      // List organization repositories
      // -------

      this.orgRepos = function(orgname, cb) {
        _request("GET", "/orgs/"+orgname+"/repos?type=all&per_page=1000&sort=updated&direction=desc", null, function(err, res) {
          cb(err, res);
        });
      };

      // Follow user
      // -------

      this.follow = function(username, cb) {
        _request("PUT", "/user/following/"+username, null, function(err, res) {
          cb(err, res);
        });
      };

      // Unfollow user
      // -------

      this.unfollow = function(username, cb) {
        _request("DELETE", "/user/following/"+username, null, function(err, res) {
          cb(err, res);
        });
      };
    };

    // Repository API
    // =======

    Github.Repository = function(options) {
      var repo = options.name;
      var user = options.user;

      var that = this;
      var repoPath = "/repos/" + user + "/" + repo;

      var currentTree = {
        "branch": null,
        "sha": null
      };

      // Uses the cache if branch has not been changed
      // -------

      function updateTree(branch, cb) {
        if (branch === currentTree.branch && currentTree.sha) return cb(null, currentTree.sha);
        that.getRef("heads/"+branch, function(err, sha) {
          currentTree.branch = branch;
          currentTree.sha = sha;
          cb(err, sha);
        });
      }

      // Get a particular reference
      // -------

      this.getRef = function(ref, cb) {
        _request("GET", repoPath + "/git/refs/" + ref, null, function(err, res) {
          if (err) return cb(err);
          cb(null, res.object.sha);
        });
      };

      // Create a new reference
      // --------
      //
      // {
      //   "ref": "refs/heads/my-new-branch-name",
      //   "sha": "827efc6d56897b048c772eb4087f854f46256132"
      // }

      this.createRef = function(options, cb) {
        _request("POST", repoPath + "/git/refs", options, cb);
      };

      // Delete a reference
      // --------
      //
      // repo.deleteRef('heads/gh-pages')
      // repo.deleteRef('tags/v1.0')

      this.deleteRef = function(ref, cb) {
        _request("DELETE", repoPath + "/git/refs/"+ref, options, cb);
      };

      // List all branches of a repository
      // -------

      this.listBranches = function(cb) {
        _request("GET", repoPath + "/git/refs/heads", null, function(err, heads) {
          if (err) return cb(err);
          cb(null, _.map(heads, function(head) { return _.last(head.ref.split('/')); }));
        });
      };

      // Retrieve the contents of a blob
      // -------

      this.getBlob = function(sha, cb) {
        _request("GET", repoPath + "/git/blobs/" + sha, null, cb, 'raw');
      };

      // For a given file path, get the corresponding sha (blob for files, tree for dirs)
      // -------

      this.getSha = function(branch, path, cb) {
        // Just use head if path is empty
        if (path === "") return that.getRef("heads/"+branch, cb);
        that.getTree(branch+"?recursive=true", function(err, tree) {
          var file = _.select(tree, function(file) {
            return file.path === path;
          })[0];
          cb(null, file ? file.sha : null);
        });
      };

      // Retrieve the tree a commit points to
      // -------

      this.getTree = function(tree, cb) {
        _request("GET", repoPath + "/git/trees/"+tree, null, function(err, res) {
          if (err) return cb(err);
          cb(null, res.tree);
        });
      };

      // Post a new blob object, getting a blob SHA back
      // -------

      this.postBlob = function(content, cb) {
        if (typeof(content) === "string") {
          content = {
            "content": content,
            "encoding": "utf-8"
          };
        }

        _request("POST", repoPath + "/git/blobs", content, function(err, res) {
          if (err) return cb(err);
          cb(null, res.sha);
        });
      };

      // Update an existing tree adding a new blob object getting a tree SHA back
      // -------

      this.updateTree = function(baseTree, path, blob, cb) {
        var data = {
          "base_tree": baseTree,
          "tree": [
            {
              "path": path,
              "mode": "100644",
              "type": "blob",
              "sha": blob
            }
          ]
        };
        _request("POST", repoPath + "/git/trees", data, function(err, res) {
          if (err) return cb(err);
          cb(null, res.sha);
        });
      };

      // Post a new tree object having a file path pointer replaced
      // with a new blob SHA getting a tree SHA back
      // -------

      this.postTree = function(tree, cb) {
        _request("POST", repoPath + "/git/trees", { "tree": tree }, function(err, res) {
          if (err) return cb(err);
          cb(null, res.sha);
        });
      };

      // Create a new commit object with the current commit SHA as the parent
      // and the new tree SHA, getting a commit SHA back
      // -------

      this.commit = function(parent, tree, message, cb) {
        var data = {
          "message": message,
          "author": {
            "name": options.username
          },
          "parents": [
            parent
          ],
          "tree": tree
        };

        _request("POST", repoPath + "/git/commits", data, function(err, res) {
          currentTree.sha = res.sha; // update latest commit
          if (err) return cb(err);
          cb(null, res.sha);
        });
      };

      // Update the reference of your head to point to the new commit SHA
      // -------

      this.updateHead = function(head, commit, cb) {
        _request("PATCH", repoPath + "/git/refs/heads/" + head, { "sha": commit }, function(err, res) {
          cb(err);
        });
      };

      // Show repository information
      // -------

      this.show = function(cb) {
        _request("GET", repoPath, null, cb);
      };

      // Get contents
      // --------

      this.contents = function(branch, path, cb) {
        _request("GET", repoPath + "/contents?ref=" + branch, { path: path }, cb);
      };

      // Fork repository
      // -------

      this.fork = function(cb) {
        _request("POST", repoPath + "/forks", null, cb);
      };

      // Create pull request
      // --------

      this.createPullRequest = function(options, cb) {
        _request("POST", repoPath + "/pulls", options, cb);
      };

      // Read file at given path
      // -------

      this.read = function(branch, path, cb) {
        that.getSha(branch, path, function(err, sha) {
          if (!sha) return cb("not found", null);
          that.getBlob(sha, function(err, content) {
            cb(err, content, sha);
          });
        });
      };

      // Remove a file from the tree
      // -------

      this.remove = function(branch, path, cb) {
        updateTree(branch, function(err, latestCommit) {
          that.getTree(latestCommit+"?recursive=true", function(err, tree) {
            // Update Tree
            var newTree = _.reject(tree, function(ref) { return ref.path === path; });
            _.each(newTree, function(ref) {
              if (ref.type === "tree") delete ref.sha;
            });

            that.postTree(newTree, function(err, rootTree) {
              that.commit(latestCommit, rootTree, 'Deleted '+path , function(err, commit) {
                that.updateHead(branch, commit, function(err) {
                  cb(err);
                });
              });
            });
          });
        });
      };

      // Move a file to a new location
      // -------

      this.move = function(branch, path, newPath, cb) {
        updateTree(branch, function(err, latestCommit) {
          that.getTree(latestCommit+"?recursive=true", function(err, tree) {
            // Update Tree
            _.each(tree, function(ref) {
              if (ref.path === path) ref.path = newPath;
              if (ref.type === "tree") delete ref.sha;
            });

            that.postTree(tree, function(err, rootTree) {
              that.commit(latestCommit, rootTree, 'Deleted '+path , function(err, commit) {
                that.updateHead(branch, commit, function(err) {
                  cb(err);
                });
              });
            });
          });
        });
      };

      // Write file contents to a given branch and path
      // -------

      this.write = function(branch, path, content, message, cb) {
        updateTree(branch, function(err, latestCommit) {
          if (err) return cb(err);
          that.postBlob(content, function(err, blob) {
            if (err) return cb(err);
            that.updateTree(latestCommit, path, blob, function(err, tree) {
              if (err) return cb(err);
              that.commit(latestCommit, tree, message, function(err, commit) {
                if (err) return cb(err);
                that.updateHead(branch, commit, cb);
              });
            });
          });
        });
      };
    };

    // Gists API
    // =======

    Github.Gist = function(options) {
      var id = options.id;
      var gistPath = "/gists/"+id;

      // Read the gist
      // --------

      this.read = function(cb) {
        _request("GET", gistPath, null, function(err, gist) {
          cb(err, gist);
        });
      };

      // Create the gist
      // --------
      // {
      //  "description": "the description for this gist",
      //    "public": true,
      //    "files": {
      //      "file1.txt": {
      //        "content": "String file contents"
      //      }
      //    }
      // }

      this.create = function(options, cb){
        _request("POST","/gists", options, cb);
      };

      // Delete the gist
      // --------

      this.delete = function(cb) {
        _request("DELETE", gistPath, null, function(err,res) {
          cb(err,res);
        });
      };

      // Fork a gist
      // --------

      this.fork = function(cb) {
        _request("POST", gistPath+"/fork", null, function(err,res) {
          cb(err,res);
        });
      };

      // Update a gist with the new stuff
      // --------

      this.update = function(options, cb) {
        _request("PATCH", gistPath, options, function(err,res) {
          cb(err,res);
        });
      };
    };

    // Top Level API
    // -------

    this.getRepo = function(user, repo) {
      return new Github.Repository({user: user, name: repo});
    };

    this.getUser = function() {
      return new Github.User();
    };

    this.getGist = function(id) {
      return new Github.Gist({id: id});
    };
  };

  if (typeof exports !== 'undefined') {
    // Github = exports;
    module.exports = Github;
  } else {
    window.Github = Github;
  }
}).call(this);
<commit_msg>added star/undstar/isStarred<commit_after>// Github.js 0.8.0
// (c) 2013 Michael Aufreiter, Development Seed
// Github.js is freely distributable under the MIT license.
// For all details and documentation:
// http://substance.io/michael/github

(function() {

  // Initial Setup
  // -------------

  var XMLHttpRequest, Base64, _;
  if (typeof exports !== 'undefined') {
      XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest;
      _ = require('underscore');
      Base64 = require('./lib/base64.js');
  }else{
      _ = window._;
      Base64 = window.Base64;
  }
  //prefer native XMLHttpRequest always
  if (typeof window !== 'undefined' && typeof window.XMLHttpRequest !== 'undefined'){
      XMLHttpRequest = window.XMLHttpRequest;
  }

  var API_URL = 'https://api.github.com';

  var Github = function(options) {

    // HTTP Request Abstraction
    // =======
    //
    // I'm not proud of this and neither should you be if you were responsible for the XMLHttpRequest spec.

    function _request(method, path, data, cb, raw) {
      function getURL() {
        var url = API_URL + path;
        return url + ((/\?/).test(url) ? "&" : "?") + (new Date()).getTime();
      }

      var xhr = new XMLHttpRequest();
      if (!raw) {xhr.dataType = "json";}

      xhr.open(method, getURL());
      xhr.onreadystatechange = function () {
        if (this.readyState == 4) {
          if (this.status >= 200 && this.status < 300 || this.status === 304) {
            cb(null, raw ? this.responseText : this.responseText ? JSON.parse(this.responseText) : true);
          } else {
            cb({request: this, error: this.status});
          }
        }
      };
      xhr.setRequestHeader('Accept','application/vnd.github.raw');
      xhr.setRequestHeader('Content-Type','application/json');
      if (
         (options.auth == 'oauth' && options.token) ||
         (options.auth == 'basic' && options.username && options.password)
         ) {
           xhr.setRequestHeader('Authorization',options.auth == 'oauth'
             ? 'token '+ options.token
             : 'Basic ' + Base64.encode(options.username + ':' + options.password)
           );
         }
      data ? xhr.send(JSON.stringify(data)) : xhr.send();
    }

    // User API
    // =======

    Github.User = function() {
      this.repos = function(cb) {
        _request("GET", "/user/repos?type=all&per_page=1000&sort=updated", null, function(err, res) {
          cb(err, res);
        });
      };

      // List user organizations
      // -------

      this.orgs = function(cb) {
        _request("GET", "/user/orgs", null, function(err, res) {
          cb(err, res);
        });
      };

      // List authenticated user's gists
      // -------

      this.gists = function(cb) {
        _request("GET", "/gists", null, function(err, res) {
          cb(err,res);
        });
      };

      // Show user information
      // -------

      this.show = function(username, cb) {
        var command = username ? "/users/"+username : "/user";

        _request("GET", command, null, function(err, res) {
          cb(err, res);
        });
      };

      // List user repositories
      // -------

      this.userRepos = function(username, cb) {
        _request("GET", "/users/"+username+"/repos?type=all&per_page=1000&sort=updated", null, function(err, res) {
          cb(err, res);
        });
      };

      // List a user's gists
      // -------

      this.userGists = function(username, cb) {
        _request("GET", "/users/"+username+"/gists", null, function(err, res) {
          cb(err,res);
        });
      };

      // List organization repositories
      // -------

      this.orgRepos = function(orgname, cb) {
        _request("GET", "/orgs/"+orgname+"/repos?type=all&per_page=1000&sort=updated&direction=desc", null, function(err, res) {
          cb(err, res);
        });
      };

      // Follow user
      // -------

      this.follow = function(username, cb) {
        _request("PUT", "/user/following/"+username, null, function(err, res) {
          cb(err, res);
        });
      };

      // Unfollow user
      // -------

      this.unfollow = function(username, cb) {
        _request("DELETE", "/user/following/"+username, null, function(err, res) {
          cb(err, res);
        });
      };
    };

    // Repository API
    // =======

    Github.Repository = function(options) {
      var repo = options.name;
      var user = options.user;

      var that = this;
      var repoPath = "/repos/" + user + "/" + repo;

      var currentTree = {
        "branch": null,
        "sha": null
      };

      // Uses the cache if branch has not been changed
      // -------

      function updateTree(branch, cb) {
        if (branch === currentTree.branch && currentTree.sha) return cb(null, currentTree.sha);
        that.getRef("heads/"+branch, function(err, sha) {
          currentTree.branch = branch;
          currentTree.sha = sha;
          cb(err, sha);
        });
      }

      // Get a particular reference
      // -------

      this.getRef = function(ref, cb) {
        _request("GET", repoPath + "/git/refs/" + ref, null, function(err, res) {
          if (err) return cb(err);
          cb(null, res.object.sha);
        });
      };

      // Create a new reference
      // --------
      //
      // {
      //   "ref": "refs/heads/my-new-branch-name",
      //   "sha": "827efc6d56897b048c772eb4087f854f46256132"
      // }

      this.createRef = function(options, cb) {
        _request("POST", repoPath + "/git/refs", options, cb);
      };

      // Delete a reference
      // --------
      //
      // repo.deleteRef('heads/gh-pages')
      // repo.deleteRef('tags/v1.0')

      this.deleteRef = function(ref, cb) {
        _request("DELETE", repoPath + "/git/refs/"+ref, options, cb);
      };

      // List all branches of a repository
      // -------

      this.listBranches = function(cb) {
        _request("GET", repoPath + "/git/refs/heads", null, function(err, heads) {
          if (err) return cb(err);
          cb(null, _.map(heads, function(head) { return _.last(head.ref.split('/')); }));
        });
      };

      // Retrieve the contents of a blob
      // -------

      this.getBlob = function(sha, cb) {
        _request("GET", repoPath + "/git/blobs/" + sha, null, cb, 'raw');
      };

      // For a given file path, get the corresponding sha (blob for files, tree for dirs)
      // -------

      this.getSha = function(branch, path, cb) {
        // Just use head if path is empty
        if (path === "") return that.getRef("heads/"+branch, cb);
        that.getTree(branch+"?recursive=true", function(err, tree) {
          var file = _.select(tree, function(file) {
            return file.path === path;
          })[0];
          cb(null, file ? file.sha : null);
        });
      };

      // Retrieve the tree a commit points to
      // -------

      this.getTree = function(tree, cb) {
        _request("GET", repoPath + "/git/trees/"+tree, null, function(err, res) {
          if (err) return cb(err);
          cb(null, res.tree);
        });
      };

      // Post a new blob object, getting a blob SHA back
      // -------

      this.postBlob = function(content, cb) {
        if (typeof(content) === "string") {
          content = {
            "content": content,
            "encoding": "utf-8"
          };
        }

        _request("POST", repoPath + "/git/blobs", content, function(err, res) {
          if (err) return cb(err);
          cb(null, res.sha);
        });
      };

      // Update an existing tree adding a new blob object getting a tree SHA back
      // -------

      this.updateTree = function(baseTree, path, blob, cb) {
        var data = {
          "base_tree": baseTree,
          "tree": [
            {
              "path": path,
              "mode": "100644",
              "type": "blob",
              "sha": blob
            }
          ]
        };
        _request("POST", repoPath + "/git/trees", data, function(err, res) {
          if (err) return cb(err);
          cb(null, res.sha);
        });
      };

      // Post a new tree object having a file path pointer replaced
      // with a new blob SHA getting a tree SHA back
      // -------

      this.postTree = function(tree, cb) {
        _request("POST", repoPath + "/git/trees", { "tree": tree }, function(err, res) {
          if (err) return cb(err);
          cb(null, res.sha);
        });
      };

      // Create a new commit object with the current commit SHA as the parent
      // and the new tree SHA, getting a commit SHA back
      // -------

      this.commit = function(parent, tree, message, cb) {
        var data = {
          "message": message,
          "author": {
            "name": options.username
          },
          "parents": [
            parent
          ],
          "tree": tree
        };

        _request("POST", repoPath + "/git/commits", data, function(err, res) {
          currentTree.sha = res.sha; // update latest commit
          if (err) return cb(err);
          cb(null, res.sha);
        });
      };

      // Update the reference of your head to point to the new commit SHA
      // -------

      this.updateHead = function(head, commit, cb) {
        _request("PATCH", repoPath + "/git/refs/heads/" + head, { "sha": commit }, function(err, res) {
          cb(err);
        });
      };

      // Show repository information
      // -------

      this.show = function(cb) {
        _request("GET", repoPath, null, cb);
      };

      // Get contents
      // --------

      this.contents = function(branch, path, cb) {
        _request("GET", repoPath + "/contents?ref=" + branch, { path: path }, cb);
      };

      // Fork repository
      // -------

      this.fork = function(cb) {
        _request("POST", repoPath + "/forks", null, cb);
      };

      // Create pull request
      // --------

      this.createPullRequest = function(options, cb) {
        _request("POST", repoPath + "/pulls", options, cb);
      };

      // Read file at given path
      // -------

      this.read = function(branch, path, cb) {
        that.getSha(branch, path, function(err, sha) {
          if (!sha) return cb("not found", null);
          that.getBlob(sha, function(err, content) {
            cb(err, content, sha);
          });
        });
      };

      // Remove a file from the tree
      // -------

      this.remove = function(branch, path, cb) {
        updateTree(branch, function(err, latestCommit) {
          that.getTree(latestCommit+"?recursive=true", function(err, tree) {
            // Update Tree
            var newTree = _.reject(tree, function(ref) { return ref.path === path; });
            _.each(newTree, function(ref) {
              if (ref.type === "tree") delete ref.sha;
            });

            that.postTree(newTree, function(err, rootTree) {
              that.commit(latestCommit, rootTree, 'Deleted '+path , function(err, commit) {
                that.updateHead(branch, commit, function(err) {
                  cb(err);
                });
              });
            });
          });
        });
      };

      // Move a file to a new location
      // -------

      this.move = function(branch, path, newPath, cb) {
        updateTree(branch, function(err, latestCommit) {
          that.getTree(latestCommit+"?recursive=true", function(err, tree) {
            // Update Tree
            _.each(tree, function(ref) {
              if (ref.path === path) ref.path = newPath;
              if (ref.type === "tree") delete ref.sha;
            });

            that.postTree(tree, function(err, rootTree) {
              that.commit(latestCommit, rootTree, 'Deleted '+path , function(err, commit) {
                that.updateHead(branch, commit, function(err) {
                  cb(err);
                });
              });
            });
          });
        });
      };

      // Write file contents to a given branch and path
      // -------

      this.write = function(branch, path, content, message, cb) {
        updateTree(branch, function(err, latestCommit) {
          if (err) return cb(err);
          that.postBlob(content, function(err, blob) {
            if (err) return cb(err);
            that.updateTree(latestCommit, path, blob, function(err, tree) {
              if (err) return cb(err);
              that.commit(latestCommit, tree, message, function(err, commit) {
                if (err) return cb(err);
                that.updateHead(branch, commit, cb);
              });
            });
          });
        });
      };
    };

    // Gists API
    // =======

    Github.Gist = function(options) {
      var id = options.id;
      var gistPath = "/gists/"+id;

      // Read the gist
      // --------

      this.read = function(cb) {
        _request("GET", gistPath, null, function(err, gist) {
          cb(err, gist);
        });
      };

      // Create the gist
      // --------
      // {
      //  "description": "the description for this gist",
      //    "public": true,
      //    "files": {
      //      "file1.txt": {
      //        "content": "String file contents"
      //      }
      //    }
      // }

      this.create = function(options, cb){
        _request("POST","/gists", options, cb);
      };

      // Delete the gist
      // --------

      this.delete = function(cb) {
        _request("DELETE", gistPath, null, function(err,res) {
          cb(err,res);
        });
      };

      // Fork a gist
      // --------

      this.fork = function(cb) {
        _request("POST", gistPath+"/fork", null, function(err,res) {
          cb(err,res);
        });
      };

      // Update a gist with the new stuff
      // --------

      this.update = function(options, cb) {
        _request("PATCH", gistPath, options, function(err,res) {
          cb(err,res);
        });
      };

      // Star a gist
      // --------

      this.star = function(cb) {
        _request("PUT", gistPath+"/star", null, function(err,res) {
          cb(err,res);
        });
      };

      // Untar a gist
      // --------

      this.unstar = function(cb) {
        _request("DELETE", gistPath+"/star", null, function(err,res) {
          cb(err,res);
        });
      };

      // Check if a gist is starred
      // --------

      this.isStarred = function(cb) {
        _request("GET", gistPath+"/star", null, function(err,res) {
          cb(err,res);
        });
      };
    };

    // Top Level API
    // -------

    this.getRepo = function(user, repo) {
      return new Github.Repository({user: user, name: repo});
    };

    this.getUser = function() {
      return new Github.User();
    };

    this.getGist = function(id) {
      return new Github.Gist({id: id});
    };
  };

  if (typeof exports !== 'undefined') {
    // Github = exports;
    module.exports = Github;
  } else {
    window.Github = Github;
  }
}).call(this);
<|endoftext|>


<commit_before>process.env.TEST_ENV = true
const config = require('../../../config.js')
const Format = require('../../../structs/db/Format.js')
const mongoose = require('mongoose')
const dbName = 'test_int_base'
const CON_OPTIONS = {
  useNewUrlParser: true,
  useUnifiedTopology: true,
  useCreateIndex: true
}

jest.mock('../../../config.js')

describe('Int::structs/db/Format', function () {
  /** @type {import('mongoose').Collection} */
  let collection
  beforeAll(async function () {
    config.database.uri = 'mongodb://'
    await mongoose.connect(`mongodb://localhost:27017/${dbName}`, CON_OPTIONS)
    await mongoose.connection.db.dropDatabase()
    collection = mongoose.connection.db.collection('formats')
  })
  describe('pruneEmbeds', function () {
    it('works with fields', function () {
      const embeds = [{
        fields: [{}, {
          name: 'abc'
        }, {
          name: 'foo',
          value: 'bar'
        }]
      }]
      const format = new Format({
        embeds
      })
      format.pruneEmbeds()
      expect(format.embeds).toEqual([{
        fields: [{
          name: 'foo',
          value: 'bar'
        }]
      }])
    })
    it('works with non-field', function () {
      const embeds = [{}, {
        title: 'dodat'
      }, {}]
      const format = new Format({
        embeds
      })
      format.pruneEmbeds()
      expect(format.embeds).toEqual([{
        title: 'dodat'
      }])
    })
  })
  it('does not save embeds with _id', async function () {
    const embeds = [{
      title: 'jack'
    }]
    const data = {
      text: 'no _id',
      embeds
    }
    const format = new Format(data)
    await format.save()
    const result = await collection.findOne({
      text: data.text
    })
    expect(result.embeds[0]).not.toHaveProperty('_id')
  })
  it('does not save embed fields with _id', async function () {
    const embeds = [{
      title: 'jack',
      fields: [{
        name: 'abas',
        value: 'w46ye5t'
      }]
    }]
    const data = {
      text: 'no _id in fields',
      embeds
    }
    const format = new Format(data)
    await format.save()
    const result = await collection.findOne({
      text: data.text
    })
    expect(result.embeds[0].fields[0]).not.toHaveProperty('_id')
  })
  afterAll(async function () {
    await mongoose.connection.db.dropDatabase()
    await mongoose.connection.close()
  })
})
<commit_msg>Fix format int tests<commit_after>process.env.TEST_ENV = true
const config = require('../../../config.js')
const Format = require('../../../structs/db/Format.js')
const mongoose = require('mongoose')
const dbName = 'test_int_base'
const CON_OPTIONS = {
  useNewUrlParser: true,
  useUnifiedTopology: true,
  useCreateIndex: true
}

jest.mock('../../../config.js')

describe('Int::structs/db/Format', function () {
  /** @type {import('mongoose').Collection} */
  let collection
  let feedID = new mongoose.Types.ObjectId()
  beforeAll(async function () {
    config.database.uri = 'mongodb://'
    await mongoose.connect(`mongodb://localhost:27017/${dbName}`, CON_OPTIONS)
    await mongoose.connection.db.dropDatabase()
    collection = mongoose.connection.db.collection('formats')
    await mongoose.connection.db.collection('feeds').insertOne({
      _id: feedID
    })
  })
  describe('pruneEmbeds', function () {

    it('works with fields', function () {
      const embeds = [{
        fields: [{}, {
          name: 'abc'
        }, {
          name: 'foo',
          value: 'bar'
        }]
      }]
      const format = new Format({
        feed: feedID.toHexString(),
        embeds
      })
      format.pruneEmbeds()
      expect(format.embeds).toEqual([{
        fields: [{
          name: 'foo',
          value: 'bar'
        }]
      }])
    })
    it('works with non-field', function () {
      const embeds = [{}, {
        title: 'dodat'
      }, {}]
      const format = new Format({
        feed: feedID.toHexString(),
        embeds
      })
      format.pruneEmbeds()
      expect(format.embeds).toEqual([{
        title: 'dodat'
      }])
    })
  })
  it('does not save embeds with _id', async function () {
    const embeds = [{
      title: 'jack'
    }]
    const data = {
      feed: feedID.toHexString(),
      text: 'no _id',
      embeds
    }
    const format = new Format(data)
    await format.save()
    const result = await collection.findOne({
      text: data.text
    })
    expect(result.embeds[0]).not.toHaveProperty('_id')
  })
  it('does not save embed fields with _id', async function () {
    const embeds = [{
      title: 'jack',
      fields: [{
        name: 'abas',
        value: 'w46ye5t'
      }]
    }]
    const data = {
      feed: feedID.toHexString(),
      text: 'no _id in fields',
      embeds
    }
    const format = new Format(data)
    await format.save()
    const result = await collection.findOne({
      text: data.text
    })
    expect(result.embeds[0].fields[0]).not.toHaveProperty('_id')
  })
  afterAll(async function () {
    await mongoose.connection.db.dropDatabase()
    await mongoose.connection.close()
  })
})
<|endoftext|>


<commit_before>import last from 'lodash/last';
import chessground from 'chessground-mobile';
import { handleXhrError } from '../../utils';
import makeData from './data';
import chess from './chess';
import puzzle from './puzzle';
import sound from '../../sound';
import settings from '../../settings';
import menu from './menu';
import * as xhr from './xhr';
import m from 'mithril';
import helper from '../helper';
import socket from '../../socket';

export default function ctrl() {

  helper.analyticsTrackView('Puzzle');
  socket.createDefault();

  this.data = null;

  this.menu = menu.controller(this);

  this.vm = {
    loading: false
  };

  const showLoading = function() {
    this.vm.loading = true;
    m.redraw();
  }.bind(this);

  const onXhrSuccess = function(res) {
    this.vm.loading = false;
    return res;
  }.bind(this);

  const onXhrError = function(res) {
    this.vm.loading = false;
    handleXhrError(res);
  }.bind(this);

  const revertLastMove = function() {
    if (this.data.progress.length === 0) {
      return;
    }
    const lastTurnColor = this.data.playHistory[this.data.playHistory.length - 1].turnColor;
    const nbPliesToRevert = lastTurnColor === this.data.player.color ? 2 : 1;
    this.data.progress = this.data.progress.slice(0, this.data.progress.length - nbPliesToRevert);
    this.data.playHistory = this.data.playHistory.slice(0, this.data.playHistory.length - nbPliesToRevert);
    const sitToRevertTo = this.data.playHistory[this.data.playHistory.length - 1];
    setTimeout(() => {
      this.chessground.set({
        fen: sitToRevertTo.fen,
        lastMove: sitToRevertTo.move,
        turnColor: sitToRevertTo.turnColor,
        check: sitToRevertTo.check,
        movable: {
          color: 'both',
          dests: sitToRevertTo.dests
        }
      });
      m.redraw();
    }, 1000);
  }.bind(this);

  const attempt = function(winFlag, giveUpFlag) {
    showLoading();
    xhr.attempt(this.data.puzzle.id, this.data.startedAt, winFlag)
    .then(cfg => {
      cfg.progress = this.data.progress;
      this.reload(cfg);
      onXhrSuccess();
    })
    .catch(err => {
      if (!giveUpFlag) {
        revertLastMove();
      }
      handleXhrError(err);
      this.vm.loading = false;
    });
  }.bind(this);

  const userMove = function(orig, dest) {
    const res = puzzle.tryMove(this.data, [orig, dest]);
    const newProgress = res[0];
    const newLines = res[1];
    const lastMove = last(newProgress);
    const promotion = lastMove ? lastMove[4] : undefined;
    switch (newLines) {
      case 'retry':
        setTimeout(this.revert.bind(this, this.data.puzzle.id), 500);
        this.data.comment = 'retry';
        break;
      case 'fail':
        setTimeout(() => {
          if (this.data.mode === 'play') {
            this.chessground.stop();
            attempt(false);
          } else {
            this.revert(this.data.puzzle.id);
          }
        }, 500);
        this.data.comment = 'fail';
        break;
      default:
        this.userFinalizeMove([orig, dest, promotion], newProgress);
        if (newLines === 'win') {
          this.chessground.stop();
          attempt(true);
        } else {
          setTimeout(this.playOpponentNextMove.bind(this, this.data.puzzle.id), 1000);
        }
        break;
    }
    m.redraw();
  }.bind(this);

  const onMove = function(orig, dest, captured) {
    if (captured) sound.capture();
    else sound.move();
  };

  this.revert = function(id) {
    if (id !== this.data.puzzle.id) return;
    this.chessground.set({
      fen: this.data.chess.fen(),
      lastMove: chess.lastMove(this.data.chess),
      turnColor: this.data.puzzle.color,
      check: null,
      movable: {
        dests: this.data.chess.dests()
      }
    });
    m.redraw();
    if (this.data.chess.in_check()) this.chessground.setCheck();
  }.bind(this);

  this.userFinalizeMove = function(move, newProgress) {
    chess.move(this.data.chess, move);
    this.data.comment = 'great';
    this.data.progress = newProgress;
    this.data.playHistory.push({
      move,
      fen: this.data.chess.fen(),
      dests: this.data.chess.dests(),
      check: this.data.chess.in_check(),
      turnColor: this.data.chess.turn() === 'w' ? 'white' : 'black'
    });
    this.chessground.set({
      fen: this.data.chess.fen(),
      lastMove: move,
      turnColor: this.data.puzzle.opponentColor,
      check: null
    });
    if (this.data.chess.in_check()) this.chessground.setCheck();
  }.bind(this);

  this.playOpponentMove = function(move) {
    onMove(move[0], move[1], this.chessground.data.pieces[move[1]]);
    chess.move(this.data.chess, move);
    this.data.playHistory.push({
      move,
      fen: this.data.chess.fen(),
      dests: this.data.chess.dests(),
      check: this.data.chess.in_check(),
      turnColor: this.data.chess.turn() === 'w' ? 'white' : 'black'
    });
    this.chessground.set({
      fen: this.data.chess.fen(),
      lastMove: move,
      movable: {
        dests: this.data.chess.dests()
      },
      turnColor: this.data.puzzle.color,
      check: null
    });
    if (this.data.chess.in_check()) this.chessground.setCheck();
    setTimeout(this.chessground.playPremove, this.chessground.data.animation.duration);
    m.redraw();
  }.bind(this);

  this.playOpponentNextMove = function(id) {
    if (id !== this.data.puzzle.id) return;
    var move = puzzle.getOpponentNextMove(this.data);
    this.playOpponentMove(puzzle.str2move(move));
    this.data.progress.push(move);
    if (puzzle.getCurrentLines(this.data) === 'win') {
      setTimeout(() => attempt(true), 300);
    }
  }.bind(this);

  this.playInitialMove = function() {
    if (this.data.mode !== 'view') {
      this.playOpponentMove(this.data.puzzle.initialMove);
      this.data.startedAt = new Date();
    }
  }.bind(this);

  this.giveUp = function() {
    attempt(false, true);
  };

  this.jump = function(to) {
    const history = this.data.replay.history;
    const step = this.data.replay.step;
    if (!(step !== to && to >= 0 && to < history.length)) return false;
    chessground.anim(puzzle.jump, this.chessground.data)(this.data, to);
    return true;
  }.bind(this);

  this.jumpFirst = this.jump.bind(this, 0);

  this.jumpPrev = function() {
    return this.jump(this.data.replay.step - 1);
  }.bind(this);

  this.jumpNext = function() {
    return this.jump(this.data.replay.step + 1);
  }.bind(this);

  this.jumpLast = function() {
    this.jump(this.data.replay.history.length - 1);
  }.bind(this);

  this.reload = function(cfg) {
    this.data = makeData(cfg);
    chessground.board.reset(this.chessground.data);
    chessground.anim(puzzle.reload, this.chessground.data)(this.data, cfg);
    setTimeout(this.playInitialMove, 1000);
  }.bind(this);

  this.init = function(cfg) {
    this.data = makeData(cfg);
    var chessgroundConf = {
      fen: this.data.puzzle.fen,
      orientation: this.data.puzzle.color,
      coordinates: settings.game.coords(),
      turnColor: this.data.puzzle.opponentColor,
      highlight: {
        lastMove: settings.game.highlights(),
        check: settings.game.highlights(),
        dragOver: false
      },
      movable: {
        free: false,
        color: this.data.mode !== 'view' ? this.data.puzzle.color : null,
        showDests: settings.game.pieceDestinations(),
        events: {
          after: userMove
        }
      },
      events: {
        move: onMove
      },
      animation: {
        enabled: true,
        duration: 300
      },
      premovable: {
        enabled: true
      },
      draggable: {
        distance: 3,
        squareTarget: true
      }
    };
    if (this.chessground) this.chessground.set(chessgroundConf);
    else this.chessground = new chessground.controller(chessgroundConf);
    m.redraw();
  }.bind(this);

  this.newPuzzle = function(feedback) {
    if (feedback) showLoading();
    xhr.newPuzzle()
    .then(cfg => {
      if (feedback) pushState(cfg);
      else replaceStateForNewPuzzle(cfg);
      this.init(cfg);
      setTimeout(this.playInitialMove, 1000);
    })
    .then(onXhrSuccess)
    .catch(onXhrError);
  }.bind(this);

  this.loadPuzzle = function(id) {
    xhr.loadPuzzle(id)
      .then(cfg => {
        this.init(cfg);
        setTimeout(this.playInitialMove, 1000);
      })
      .then(onXhrSuccess)
      .catch(onXhrError);
  }.bind(this);

  this.retry = function() {
    showLoading();
    xhr.loadPuzzle(this.data.puzzle.id)
      .then(this.reload)
      .then(onXhrSuccess)
      .catch(onXhrError);
  }.bind(this);

  this.share = function() {
    window.plugins.socialsharing.share(null, null, null, `http://lichess.org/training/${this.data.puzzle.id}`);
  }.bind(this);

  this.setDifficulty = function(id) {
    return xhr.setDifficulty(id)
      .then(pushState)
      .then(this.reload);
  }.bind(this);

  if (m.route.param('id')) {
    this.loadPuzzle(m.route.param('id'));
  } else {
    this.newPuzzle(false);
  }

  window.plugins.insomnia.keepAwake();

  this.onunload = function() {
    window.plugins.insomnia.allowSleepAgain();
  };
}

function pushState(cfg) {
  window.history.pushState(null, null, '?/training/' + cfg.puzzle.id);
  return cfg;
}

function replaceStateForNewPuzzle(cfg) {
  // ugly hack to bypass mithril's postRedraw hook
  setTimeout(function() {
    window.history.replaceState(null, null, '?/training/' + cfg.puzzle.id);
  }, 100);
  return cfg;
}
<commit_msg>Custom error message for puzzle attempt fail<commit_after>import last from 'lodash/last';
import chessground from 'chessground-mobile';
import { handleXhrError } from '../../utils';
import makeData from './data';
import chess from './chess';
import puzzle from './puzzle';
import sound from '../../sound';
import settings from '../../settings';
import menu from './menu';
import * as xhr from './xhr';
import m from 'mithril';
import helper from '../helper';
import socket from '../../socket';

export default function ctrl() {

  helper.analyticsTrackView('Puzzle');
  socket.createDefault();

  this.data = null;

  this.menu = menu.controller(this);

  this.vm = {
    loading: false
  };

  const showLoading = function() {
    this.vm.loading = true;
    m.redraw();
  }.bind(this);

  const onXhrSuccess = function(res) {
    this.vm.loading = false;
    return res;
  }.bind(this);

  const onXhrError = function(res) {
    this.vm.loading = false;
    handleXhrError(res);
  }.bind(this);

  const revertLastMove = function() {
    if (this.data.progress.length === 0) {
      return;
    }
    const lastTurnColor = this.data.playHistory[this.data.playHistory.length - 1].turnColor;
    const nbPliesToRevert = lastTurnColor === this.data.player.color ? 2 : 1;
    this.data.progress = this.data.progress.slice(0, this.data.progress.length - nbPliesToRevert);
    this.data.playHistory = this.data.playHistory.slice(0, this.data.playHistory.length - nbPliesToRevert);
    const sitToRevertTo = this.data.playHistory[this.data.playHistory.length - 1];
    setTimeout(() => {
      this.chessground.set({
        fen: sitToRevertTo.fen,
        lastMove: sitToRevertTo.move,
        turnColor: sitToRevertTo.turnColor,
        check: sitToRevertTo.check,
        movable: {
          color: 'both',
          dests: sitToRevertTo.dests
        }
      });
      m.redraw();
    }, 1000);
  }.bind(this);

  const attempt = function(winFlag, giveUpFlag) {
    showLoading();
    xhr.attempt(this.data.puzzle.id, this.data.startedAt, winFlag)
    .then(cfg => {
      cfg.progress = this.data.progress;
      this.reload(cfg);
      onXhrSuccess();
    })
    .catch(() => {
      if (!giveUpFlag) {
        revertLastMove();
      }
      const msg = 'Your move has failed to reach lichess server. Please retry to move when the network is back.';
      window.plugins.toast.show(msg, 'long', 'center');
      this.vm.loading = false;
    });
  }.bind(this);

  const userMove = function(orig, dest) {
    const res = puzzle.tryMove(this.data, [orig, dest]);
    const newProgress = res[0];
    const newLines = res[1];
    const lastMove = last(newProgress);
    const promotion = lastMove ? lastMove[4] : undefined;
    switch (newLines) {
      case 'retry':
        setTimeout(this.revert.bind(this, this.data.puzzle.id), 500);
        this.data.comment = 'retry';
        break;
      case 'fail':
        setTimeout(() => {
          if (this.data.mode === 'play') {
            this.chessground.stop();
            attempt(false);
          } else {
            this.revert(this.data.puzzle.id);
          }
        }, 500);
        this.data.comment = 'fail';
        break;
      default:
        this.userFinalizeMove([orig, dest, promotion], newProgress);
        if (newLines === 'win') {
          this.chessground.stop();
          attempt(true);
        } else {
          setTimeout(this.playOpponentNextMove.bind(this, this.data.puzzle.id), 1000);
        }
        break;
    }
    m.redraw();
  }.bind(this);

  const onMove = function(orig, dest, captured) {
    if (captured) sound.capture();
    else sound.move();
  };

  this.revert = function(id) {
    if (id !== this.data.puzzle.id) return;
    this.chessground.set({
      fen: this.data.chess.fen(),
      lastMove: chess.lastMove(this.data.chess),
      turnColor: this.data.puzzle.color,
      check: null,
      movable: {
        dests: this.data.chess.dests()
      }
    });
    m.redraw();
    if (this.data.chess.in_check()) this.chessground.setCheck();
  }.bind(this);

  this.userFinalizeMove = function(move, newProgress) {
    chess.move(this.data.chess, move);
    this.data.comment = 'great';
    this.data.progress = newProgress;
    this.data.playHistory.push({
      move,
      fen: this.data.chess.fen(),
      dests: this.data.chess.dests(),
      check: this.data.chess.in_check(),
      turnColor: this.data.chess.turn() === 'w' ? 'white' : 'black'
    });
    this.chessground.set({
      fen: this.data.chess.fen(),
      lastMove: move,
      turnColor: this.data.puzzle.opponentColor,
      check: null
    });
    if (this.data.chess.in_check()) this.chessground.setCheck();
  }.bind(this);

  this.playOpponentMove = function(move) {
    onMove(move[0], move[1], this.chessground.data.pieces[move[1]]);
    chess.move(this.data.chess, move);
    this.data.playHistory.push({
      move,
      fen: this.data.chess.fen(),
      dests: this.data.chess.dests(),
      check: this.data.chess.in_check(),
      turnColor: this.data.chess.turn() === 'w' ? 'white' : 'black'
    });
    this.chessground.set({
      fen: this.data.chess.fen(),
      lastMove: move,
      movable: {
        dests: this.data.chess.dests()
      },
      turnColor: this.data.puzzle.color,
      check: null
    });
    if (this.data.chess.in_check()) this.chessground.setCheck();
    setTimeout(this.chessground.playPremove, this.chessground.data.animation.duration);
    m.redraw();
  }.bind(this);

  this.playOpponentNextMove = function(id) {
    if (id !== this.data.puzzle.id) return;
    var move = puzzle.getOpponentNextMove(this.data);
    this.playOpponentMove(puzzle.str2move(move));
    this.data.progress.push(move);
    if (puzzle.getCurrentLines(this.data) === 'win') {
      setTimeout(() => attempt(true), 300);
    }
  }.bind(this);

  this.playInitialMove = function() {
    if (this.data.mode !== 'view') {
      this.playOpponentMove(this.data.puzzle.initialMove);
      this.data.startedAt = new Date();
    }
  }.bind(this);

  this.giveUp = function() {
    attempt(false, true);
  };

  this.jump = function(to) {
    const history = this.data.replay.history;
    const step = this.data.replay.step;
    if (!(step !== to && to >= 0 && to < history.length)) return false;
    chessground.anim(puzzle.jump, this.chessground.data)(this.data, to);
    return true;
  }.bind(this);

  this.jumpFirst = this.jump.bind(this, 0);

  this.jumpPrev = function() {
    return this.jump(this.data.replay.step - 1);
  }.bind(this);

  this.jumpNext = function() {
    return this.jump(this.data.replay.step + 1);
  }.bind(this);

  this.jumpLast = function() {
    this.jump(this.data.replay.history.length - 1);
  }.bind(this);

  this.reload = function(cfg) {
    this.data = makeData(cfg);
    chessground.board.reset(this.chessground.data);
    chessground.anim(puzzle.reload, this.chessground.data)(this.data, cfg);
    setTimeout(this.playInitialMove, 1000);
  }.bind(this);

  this.init = function(cfg) {
    this.data = makeData(cfg);
    var chessgroundConf = {
      fen: this.data.puzzle.fen,
      orientation: this.data.puzzle.color,
      coordinates: settings.game.coords(),
      turnColor: this.data.puzzle.opponentColor,
      highlight: {
        lastMove: settings.game.highlights(),
        check: settings.game.highlights(),
        dragOver: false
      },
      movable: {
        free: false,
        color: this.data.mode !== 'view' ? this.data.puzzle.color : null,
        showDests: settings.game.pieceDestinations(),
        events: {
          after: userMove
        }
      },
      events: {
        move: onMove
      },
      animation: {
        enabled: true,
        duration: 300
      },
      premovable: {
        enabled: true
      },
      draggable: {
        distance: 3,
        squareTarget: true
      }
    };
    if (this.chessground) this.chessground.set(chessgroundConf);
    else this.chessground = new chessground.controller(chessgroundConf);
    m.redraw();
  }.bind(this);

  this.newPuzzle = function(feedback) {
    if (feedback) showLoading();
    xhr.newPuzzle()
    .then(cfg => {
      if (feedback) pushState(cfg);
      else replaceStateForNewPuzzle(cfg);
      this.init(cfg);
      setTimeout(this.playInitialMove, 1000);
    })
    .then(onXhrSuccess)
    .catch(onXhrError);
  }.bind(this);

  this.loadPuzzle = function(id) {
    xhr.loadPuzzle(id)
      .then(cfg => {
        this.init(cfg);
        setTimeout(this.playInitialMove, 1000);
      })
      .then(onXhrSuccess)
      .catch(onXhrError);
  }.bind(this);

  this.retry = function() {
    showLoading();
    xhr.loadPuzzle(this.data.puzzle.id)
      .then(this.reload)
      .then(onXhrSuccess)
      .catch(onXhrError);
  }.bind(this);

  this.share = function() {
    window.plugins.socialsharing.share(null, null, null, `http://lichess.org/training/${this.data.puzzle.id}`);
  }.bind(this);

  this.setDifficulty = function(id) {
    return xhr.setDifficulty(id)
      .then(pushState)
      .then(this.reload);
  }.bind(this);

  if (m.route.param('id')) {
    this.loadPuzzle(m.route.param('id'));
  } else {
    this.newPuzzle(false);
  }

  window.plugins.insomnia.keepAwake();

  this.onunload = function() {
    window.plugins.insomnia.allowSleepAgain();
  };
}

function pushState(cfg) {
  window.history.pushState(null, null, '?/training/' + cfg.puzzle.id);
  return cfg;
}

function replaceStateForNewPuzzle(cfg) {
  // ugly hack to bypass mithril's postRedraw hook
  setTimeout(function() {
    window.history.replaceState(null, null, '?/training/' + cfg.puzzle.id);
  }, 100);
  return cfg;
}
<|endoftext|>


<commit_before>/*

This file exists because we sometimes need to refer to URL's that live in the
Angular app. Better to contain all that kind of coupling here than to spread it
throughout the code.

*/

const prefix = `${process.env.PROTOCOL}://${process.env.DOMAIN}`

var url = function () {
  var args = Array.prototype.slice.call(arguments)
  args[0] = prefix + args[0]
  return format.apply(null, args)
}

module.exports = {
  Route: {
    community: function (community) {
      return url('/c/%s', community.get('slug'))
    },

    profile: function (user) {
      return url('/u/%s', user.id)
    },

    project: function (project) {
      return url('/project/%s', project.get('slug'))
    },

    projectSettings: function (project) {
      return url('/project/%s/settings', project.get('slug'))
    },

    post: function (post) {
      return url(`/p/${post.id}`)
    },

    unfollow: function (post, community) {
      return this.post(post, community) + '?action=unfollow'
    },

    userSettings: function () {
      return url('/settings')
    },

    tokenLogin: function (user, token, nextUrl) {
      return url('/noo/login/token?u=%s&t=%s&n=%s',
        user.id, token, encodeURIComponent(nextUrl || ''))
    },

    error: function (key) {
      return url('/error?key=' + encodeURIComponent(key))
    },

    useInvitation: function (token) {
      return url('/h/use-invitation?token=%s', token)
    }
  }
}
<commit_msg>use correct front-end route for project<commit_after>/*

This file exists because we sometimes need to refer to URL's that live in the
Angular app. Better to contain all that kind of coupling here than to spread it
throughout the code.

*/

const prefix = `${process.env.PROTOCOL}://${process.env.DOMAIN}`

var url = function () {
  var args = Array.prototype.slice.call(arguments)
  args[0] = prefix + args[0]
  return format.apply(null, args)
}

module.exports = {
  Route: {
    community: function (community) {
      return url('/c/%s', community.get('slug'))
    },

    profile: function (user) {
      return url('/u/%s', user.id)
    },

    project: function (project) {
      return url('/project/%s/%s', project.id, project.get('slug'))
    },

    projectSettings: function (project) {
      return url('/project/%s/settings', project.get('slug'))
    },

    post: function (post) {
      return url(`/p/${post.id}`)
    },

    unfollow: function (post, community) {
      return this.post(post, community) + '?action=unfollow'
    },

    userSettings: function () {
      return url('/settings')
    },

    tokenLogin: function (user, token, nextUrl) {
      return url('/noo/login/token?u=%s&t=%s&n=%s',
        user.id, token, encodeURIComponent(nextUrl || ''))
    },

    error: function (key) {
      return url('/error?key=' + encodeURIComponent(key))
    },

    useInvitation: function (token) {
      return url('/h/use-invitation?token=%s', token)
    }
  }
}
<|endoftext|>