<commit_before>'use strict';

import Morph from './Morph.js';

export default class Sync extends Morph {
  initialize() {
    this.windowTitle = "GitHub Sync"
    var container = this.q(".container");
    lively.html.registerButtons(this)
    lively.html.registerInputs(this)
    this.updateLoginStatus()

    if (window.__karma__) {
      console.log("exit early... due to karma")
      return
    }

    this.getSubmorph('#gitrepository').value = lively4url.replace(/.*\//,"")

    var travis = this.shadowRoot.querySelector("#travisLink")
    travis.onclick = () => {
      window.open(travis.getAttribute("href"))
      return false
    }
  }

  // #TODO pull into Morph?
  q(selector) {
    return this.shadowRoot.querySelector(selector)
  }

  // #TODO into Morph or Tool
  clearLog(s) {
    var editor= this.q("#log").editor
    if (editor) editor.setValue("")
  }

  log(s) {
    var editor = this.q("#log").editor
    if (editor) editor.setValue(editor.getValue() + "\n" + s)
  }

  async updateLoginStatus() {
    if (window.__karma__) return; // no lively4-server active

    // this.updateLoginStatus()
    var token = await this.loadValue("githubToken")
    this.q("#loginButton").innerHTML =
        token ? "logout" : "login";
    var login = token ? true : false;
    this.loggedin = login;

    this.q("#gitusername").value =
      await this.loadValue("githubUsername");
    this.q("#gitemail").value =
      await this.loadValue("githubEmail");

    var value = await this.loadValue("githubRepository")
    if (value)this.q("#gitrepository").value = value;

    this.updateContextSensitiveButtons();
    this.updateRepositoryList();
    this.updateBranchesList();
  }

  githubApi(path, token) {
    return fetch("https://api.github.com" + path, {headers: new Headers({
      Authorization: "token " + token
    })}).then(r => r.json())
  }

  async login() {
    this.loadValue("githubToken").then((result) => {
      if (result) return result
      return new Promise((resolve, reject) => {
        lively.authGithub.challengeForAuth(Date.now(), async (token) => {
          console.log("authenticated")
          var user = await this.githubApi("/user", token)
          var username = user.login
          var emails =  await this.githubApi("/user/emails", token)
          var email = emails.find(ea => ea.primary).email

          console.log("username: " + username )
          console.log("email: " + email )

          this.storeValue("githubUsername", username);
      	  this.storeValue("githubEmail", email);
      	  this.storeValue("githubToken", token);
      	  this.updateLoginStatus();
      	  resolve(token);
        });
      });
    }).then((token) => {
      this.log("Logged in");
    });
  }

  async getHeaders() {
    return new Headers({
      "gitrepository":        this.q("#gitrepository").value,
      "gitusername":          this.q("#gitusername").value,
      "gitpassword":          await this.loadValue("githubToken"),
      "gitemail":             this.q("#gitemail").value,
      "gitrepositoryurl":     this.q("#gitrepositoryurl").value,
	    "gitrepository":        this.q("#gitrepository").value,
	    "gitrepositorybranch":  this.q("#gitrepositorybranch").value,
	    "gitcommitmessage":     this.q("#gitcommitmessage").value,
	    "dryrun":               this.q("#dryrun").checked
    })
  }

  getServerURL() {
      return this.serverURL || lively4url.match(/(.*)\/([^\/]+$)/)[1]
  }

  setServerURL(url) {
      this.serverURL = url
  }

  setRepository(name) {
     this.q("#gitrepository").value = name
  }

  getRepository(name) {
     return this.q("#gitrepository").value
  }

  async gitControl(cmd, eachCB) {
    this.clearLog()
    return new Promise(async (resolve) => {
      lively.files.fetchChunks(fetch(this.getServerURL() +"/_git/" + cmd, {
              headers: await this.getHeaders()
            }), (eaChunk) => {
          if (eachCB)
            eachCB(eaChunk)
          else
            this.log("" + eaChunk)
        }, resolve)
    })
  }

  onSyncButton() {
    this.gitControl("status").then((status) => {
      if (!status.match("AUTO-COMMIT-")) {
        this.sync()
        lively.notify("sync directly")
      } else {
        if (window.confirm("Contains auto commits. Forgot to squash? Push them anyway? ")) {
          this.sync()
          lively.notify("sync anyway")
        } else {
          lively.notify("sync canceled")
        }
      }
    })
  }

  sync() {
    return this.gitControl("sync")
  }

  async onLoginButton() {
    this.clearLog()
    if (await this.loadValue("githubToken")) {
      this.logout()
    } else {
      this.login()
    }
  }

  onBranchButton() {
    this.gitControl("branch")
  }

  onStatusButton() {
    this.gitControl("status")
  }

  onDiffButton() {
    this.gitControl("diff")
  }

  async onCloneButton(){
    if (window.confirm("Do you want to clone into " +
        this.q("#gitrepository").value)) {
      this.q("#cloneButton").disabled= true
      await this.gitControl("clone")
      this.updateContextSensitiveButtons()
      this.updateRepositoryList()
    }
  }

  onNpmInstallButton() {
    this.gitControl("npminstall")
  }

  onResolveButton() {
    this.gitControl("resolve")
  }

  onChangelogButton() {
   this.gitControl("log");
  }

  onCommitButton() {
    // return lively.notify("Commit is not implemented yet")
    this.gitControl("commit");
  }

  onDeleteButton() {
    if (window.confirm("Do you want to delete " + this.q("#gitrepository").value + " repository?")) {
      this.gitControl("delete");
    }
  }

  onMergeButton() {
    if (window.confirm("Do you want to merge "
      + this.q("#gitrepositorybranch").value
      +" into " + this.q("#gitrepository").value
      + " repository?")) {
      this.gitControl("merge");
    }
  }

  onSquashButton() {
    this.gitControl("squash");
  }

  get storagePrefix() {
    return "LivelySync_"
  }

  async storeValue(key, value) {
    return  lively.focalStorage.setItem(this.storagePrefix + key, value)
  }

  async loadValue(key) {
    return lively.focalStorage.getItem(this.storagePrefix + key)
  }

  logout() {
    this.clearLog()
  	this.storeValue("githubToken", null)
  	this.storeValue("githubUsername", null)
  	this.storeValue("githubEmail", null)
    this.updateLoginStatus()
    this.log("")
  }

  async getGitRepositoryNames() {
    var json = await lively.files.statFile(this.getServerURL()).then( JSON.parse)
    return json.contents.filter(ea => ea.type == "directory").map(ea => ea.name)
  }

  async updateUpstreamURL() {
    var url = await this.gitControl("remoteurl")
    url = url.replace(/\n/,"")
    this.shadowRoot.querySelector("#gitrepositoryurl").value = url
  }

  async onGitrepositoryChanged(value) {
    this.updateContextSensitiveButtons()
  }

  async updateRepositoryList() {
    var list = await this.getGitRepositoryNames()
    this.shadowRoot.querySelector("#gitrepositories").innerHTML =
      list.map(ea => "<option>" + ea).join("\n")
  }

  async updateBranchesList() {
    var branches = await this.gitControl("branches", ()=>{})
    branches = branches.split("\n")
    var currentRegex = /^ *\*/
    var currentBranch = _.detect(branches, ea => ea.match(currentRegex))
    if(!currentBranch) {
      return
    }
    currentBranch = currentBranch.replace(currentRegex,"")
    this.q("#gitrepositorybranch").value = currentBranch

    var remoteRegEx = /^remotes\/origin\//
    branches = branches
      .map(ea => ea.replace(/^\*? */,"")) // trim
      .filter(ea => ea.match(remoteRegEx))
      .filter(ea => ! ea.match("HEAD "))
      .map(ea => ea.replace(remoteRegEx,""))
    this.q("#gitbranches").innerHTML = branches.map(ea => "<option>" + ea).join("\n")
    console.log("branches: " + branches)
  }

  get repositoryBlacklist() {
    return ["lively4-core", "lively4-stable"]
  }

  async updateContextSensitiveButtons() {

    var repository = this.q("#gitrepository").value
    var list = await this.getGitRepositoryNames()
    var exists = _.include(list, repository)

    console.log("delete " + this.q("#deleteButton").disabled)

    if (exists) {

      this.updateUpstreamURL()
      this.updateBranchesList()
    }

    _.each(this.shadowRoot.querySelectorAll(".repo"), ea =>
      ea.disabled= !this.loggedin || !exists)

    _.each(this.shadowRoot.querySelectorAll(".branch"), ea =>
      ea.disabled= !this.loggedin)

    _.each(this.shadowRoot.querySelectorAll(".clone"), ea =>
      ea.disabled= !this.loggedin || exists)

    _.each(this.shadowRoot.querySelectorAll(".login"), ea =>
      ea.disabled= this.loggedin)

    if (_.include(this.repositoryBlacklist, repository))
      this.q("#deleteButton").disabled = true
  }
}
<commit_msg>[sync] formatting<commit_after>'use strict';

import Morph from './Morph.js';

export default class Sync extends Morph {
  initialize() {
    this.windowTitle = "GitHub Sync";
    var container = this.get(".container");
    lively.html.registerButtons(this);
    lively.html.registerInputs(this);
    this.updateLoginStatus();

    if (window.__karma__) {
      console.log("exit early... due to karma");
      return;
    }

    this.getSubmorph('#gitrepository').value = lively4url.replace(/.*\//,"");

    var travis = this.shadowRoot.querySelector("#travisLink");
    travis.onclick = () => {
      window.open(travis.getAttribute("href"));
      return false;
    };
  }

  // #TODO into Morph or Tool
  clearLog(s) {
    var editor= this.get("#log").editor;
    if (editor) editor.setValue("");
  }

  log(s) {
    var editor = this.get("#log").editor;
    if (editor) editor.setValue(editor.getValue() + "\n" + s);
  }

  async updateLoginStatus() {
    if (window.__karma__) return; // no lively4-server active

    // this.updateLoginStatus()
    var token = await this.loadValue("githubToken")
    this.get("#loginButton").innerHTML =
        token ? "logout" : "login";
    var login = token ? true : false;
    this.loggedin = login;

    this.get("#gitusername").value =
      await this.loadValue("githubUsername");
    this.get("#gitemail").value =
      await this.loadValue("githubEmail");

    var value = await this.loadValue("githubRepository")
    if (value)this.get("#gitrepository").value = value;

    this.updateContextSensitiveButtons();
    this.updateRepositoryList();
    this.updateBranchesList();
  }

  githubApi(path, token) {
    return fetch("https://api.github.com" + path, {headers: new Headers({
      Authorization: "token " + token
    })}).then(r => r.json());
  }

  async login() {
    this.loadValue("githubToken").then((result) => {
      if (result) return result;
      return new Promise((resolve, reject) => {
        lively.authGithub.challengeForAuth(Date.now(), async (token) => {
          console.log("authenticated");
          var user = await this.githubApi("/user", token);
          var username = user.login;
          var emails =  await this.githubApi("/user/emails", token);
          var email = emails.find(ea => ea.primary).email;

          console.log("username: " + username);
          console.log("email: " + email);

          this.storeValue("githubUsername", username);
      	  this.storeValue("githubEmail", email);
      	  this.storeValue("githubToken", token);
      	  this.updateLoginStatus();
      	  resolve(token);
        });
      });
    }).then((token) => {
      this.log("Logged in");
    });
  }

  async getHeaders() {
    return new Headers({
      "gitrepository":        this.get("#gitrepository").value,
      "gitusername":          this.get("#gitusername").value,
      "gitpassword":          await this.loadValue("githubToken"),
      "gitemail":             this.get("#gitemail").value,
      "gitrepositoryurl":     this.get("#gitrepositoryurl").value,
	    "gitrepository":        this.get("#gitrepository").value,
	    "gitrepositorybranch":  this.get("#gitrepositorybranch").value,
	    "gitcommitmessage":     this.get("#gitcommitmessage").value,
	    "dryrun":               this.get("#dryrun").checked
    })
  }

  getServerURL() {
      return this.serverURL || lively4url.match(/(.*)\/([^\/]+$)/)[1]
  }

  setServerURL(url) {
      this.serverURL = url
  }

  setRepository(name) {
     this.get("#gitrepository").value = name
  }

  getRepository(name) {
     return this.get("#gitrepository").value
  }

  async gitControl(cmd, eachCB) {
    this.clearLog()
    return new Promise(async (resolve) => {
      lively.files.fetchChunks(fetch(this.getServerURL() +"/_git/" + cmd, {
              headers: await this.getHeaders()
            }), (eaChunk) => {
          if (eachCB)
            eachCB(eaChunk)
          else
            this.log("" + eaChunk)
        }, resolve)
    })
  }

  onSyncButton() {
    this.gitControl("status").then((status) => {
      if (!status.match("AUTO-COMMIT-")) {
        this.sync()
        lively.notify("sync directly")
      } else {
        if (window.confirm("Contains auto commits. Forgot to squash? Push them anyway? ")) {
          this.sync()
          lively.notify("sync anyway")
        } else {
          lively.notify("sync canceled")
        }
      }
    })
  }

  sync() {
    return this.gitControl("sync")
  }

  async onLoginButton() {
    this.clearLog()
    if (await this.loadValue("githubToken")) {
      this.logout()
    } else {
      this.login()
    }
  }

  onBranchButton() {
    this.gitControl("branch")
  }

  onStatusButton() {
    this.gitControl("status")
  }

  onDiffButton() {
    this.gitControl("diff")
  }

  async onCloneButton(){
    if (window.confirm("Do you want to clone into " +
        this.get("#gitrepository").value)) {
      this.get("#cloneButton").disabled= true
      await this.gitControl("clone")
      this.updateContextSensitiveButtons()
      this.updateRepositoryList()
    }
  }

  onNpmInstallButton() {
    this.gitControl("npminstall")
  }

  onResolveButton() {
    this.gitControl("resolve")
  }

  onChangelogButton() {
   this.gitControl("log");
  }

  onCommitButton() {
    // return lively.notify("Commit is not implemented yet")
    this.gitControl("commit");
  }

  onDeleteButton() {
    if (window.confirm("Do you want to delete " + this.get("#gitrepository").value + " repository?")) {
      this.gitControl("delete");
    }
  }

  onMergeButton() {
    if (window.confirm("Do you want to merge "
      + this.get("#gitrepositorybranch").value
      +" into " + this.get("#gitrepository").value
      + " repository?")) {
      this.gitControl("merge");
    }
  }

  onSquashButton() {
    this.gitControl("squash");
  }

  get storagePrefix() {
    return "LivelySync_"
  }

  async storeValue(key, value) {
    return  lively.focalStorage.setItem(this.storagePrefix + key, value)
  }

  async loadValue(key) {
    return lively.focalStorage.getItem(this.storagePrefix + key)
  }

  logout() {
    this.clearLog()
  	this.storeValue("githubToken", null)
  	this.storeValue("githubUsername", null)
  	this.storeValue("githubEmail", null)
    this.updateLoginStatus()
    this.log("")
  }

  async getGitRepositoryNames() {
    var json = await lively.files.statFile(this.getServerURL()).then( JSON.parse)
    return json.contents.filter(ea => ea.type == "directory").map(ea => ea.name)
  }

  async updateUpstreamURL() {
    var url = await this.gitControl("remoteurl")
    url = url.replace(/\n/,"")
    this.shadowRoot.querySelector("#gitrepositoryurl").value = url
  }

  async onGitrepositoryChanged(value) {
    this.updateContextSensitiveButtons()
  }

  async updateRepositoryList() {
    var list = await this.getGitRepositoryNames()
    this.shadowRoot.querySelector("#gitrepositories").innerHTML =
      list.map(ea => "<option>" + ea).join("\n")
  }

  async updateBranchesList() {
    var branches = await this.gitControl("branches", ()=>{})
    branches = branches.split("\n")
    var currentRegex = /^ *\*/
    var currentBranch = _.detect(branches, ea => ea.match(currentRegex))
    if(!currentBranch) {
      return
    }
    currentBranch = currentBranch.replace(currentRegex,"")
    this.get("#gitrepositorybranch").value = currentBranch

    var remoteRegEx = /^remotes\/origin\//
    branches = branches
      .map(ea => ea.replace(/^\*? */,"")) // trim
      .filter(ea => ea.match(remoteRegEx))
      .filter(ea => ! ea.match("HEAD "))
      .map(ea => ea.replace(remoteRegEx,""))
    this.get("#gitbranches").innerHTML = branches.map(ea => "<option>" + ea).join("\n")
    console.log("branches: " + branches)
  }

  get repositoryBlacklist() {
    return ["lively4-core", "lively4-stable"]
  }

  async updateContextSensitiveButtons() {

    var repository = this.get("#gitrepository").value
    var list = await this.getGitRepositoryNames()
    var exists = _.include(list, repository)

    console.log("delete " + this.get("#deleteButton").disabled)

    if (exists) {

      this.updateUpstreamURL()
      this.updateBranchesList()
    }

    _.each(this.shadowRoot.querySelectorAll(".repo"), ea =>
      ea.disabled= !this.loggedin || !exists)

    _.each(this.shadowRoot.querySelectorAll(".branch"), ea =>
      ea.disabled= !this.loggedin)

    _.each(this.shadowRoot.querySelectorAll(".clone"), ea =>
      ea.disabled= !this.loggedin || exists)

    _.each(this.shadowRoot.querySelectorAll(".login"), ea =>
      ea.disabled= this.loggedin)

    if (_.include(this.repositoryBlacklist, repository))
      this.get("#deleteButton").disabled = true
  }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* eslint max-lines: off*/
(function(){
	"use strict";
	var settingsDisplay = [
		{
			"name": "displayAdvancedSettings"
		},
		{
			"name": "displayDescriptions"
		},
		{
			"name": "blockMode"
		},
		"asking",
		{
			"name": "askOnlyOnce",
			"displayDependencies": {
				"blockMode": ["askReadout", "ask"]
			}
		},
		{
			"name": "askDenyMode",
			"displayDependencies": {
				"blockMode": ["askReadout", "ask"],
				"displayAdvancedSettings": [true]
			}
		},
		{
			"name": "showCanvasWhileAsking",
			"displayDependencies": {
				"blockMode": ["askReadout", "ask"],
				"displayAdvancedSettings": [true]
			}
		},
		"faking",
		{
			"name": "minFakeSize",
			"displayDependencies": [
				{
					"blockMode": ["fakeReadout", "fakeInput"],
					"displayAdvancedSettings": [true]
				},
				{
					"blockMode": ["askReadout", "ask"],
					"askDenyMode": ["fake"],
					"displayAdvancedSettings": [true]
				}
			]
		},
		{
			"name": "maxFakeSize",
			"displayDependencies": [
				{
					"blockMode": ["fakeReadout", "fakeInput"],
					"displayAdvancedSettings": [true]
				},
				{
					"blockMode": ["askReadout", "ask"],
					"askDenyMode": ["fake"],
					"displayAdvancedSettings": [true]
				}
			]
		},
		{
			"name": "rng",
			"displayDependencies": [
				{
					"blockMode": ["fakeReadout", "fakeInput"],
					"displayAdvancedSettings": [true]
				},
				{
					"blockMode": ["askReadout", "ask"],
					"askDenyMode": ["fake"],
					"displayAdvancedSettings": [true]
				}
			]
		},
		{
			"name": "storePersistentRnd",
			"displayDependencies": [
				{
					"blockMode": ["fakeReadout", "fakeInput"],
					"rng": ["persistent"],
					"displayAdvancedSettings": [true]
				},
				{
					"blockMode": ["askReadout", "ask"],
					"askDenyMode": ["fake"],
					"rng": ["persistent"],
					"displayAdvancedSettings": [true]
				}
			]
		},
		{
			"name": "persistentRndClearInterval",
			"inputs": ["persistentRndClearIntervalValue", "persistentRndClearIntervalUnit"],
			"displayDependencies": [
				{
					"blockMode": ["fakeReadout", "fakeInput"],
					"rng": ["persistent"],
					"displayAdvancedSettings": [true]
				},
				{
					"blockMode": ["askReadout", "ask"],
					"askDenyMode": ["fake"],
					"rng": ["persistent"],
					"displayAdvancedSettings": [true]
				}
			]
		},
		{
			"name": "clearPersistentRnd",
			"displayDependencies": [
				{
					"blockMode": ["fakeReadout", "fakeInput"],
					"rng": ["persistent"],
					"displayAdvancedSettings": [true]
				},
				{
					"blockMode": ["askReadout", "ask"],
					"askDenyMode": ["fake"],
					"rng": ["persistent"],
					"displayAdvancedSettings": [true]
				}
			]
		},
		{
			"name": "ignoreFrequentColors",
			"displayDependencies": [
				{
					"blockMode": ["fakeReadout"],
					"displayAdvancedSettings": [true]
				},
				{
					"blockMode": ["askReadout", "ask"],
					"askDenyMode": ["fake"],
					"displayAdvancedSettings": [true]
				}
			]
		},
		{
			"name": "minColors",
			"displayDependencies": [
				{
					"blockMode": ["fakeReadout"],
					"displayAdvancedSettings": [true]
				},
				{
					"blockMode": ["askReadout", "ask"],
					"askDenyMode": ["fake"],
					"displayAdvancedSettings": [true]
				}
			]
		},
		{
			"name": "fakeAlphaChannel",
			"displayDependencies": [
				{
					"blockMode": ["fakeReadout"],
					"displayAdvancedSettings": [true]
				},
				{
					"blockMode": ["askReadout", "ask"],
					"askDenyMode": ["fake"],
					"displayAdvancedSettings": [true]
				}
			]
		},
		{
			"name": "useCanvasCache",
			"displayDependencies": [
				{
					"blockMode": ["fakeReadout"],
					"displayAdvancedSettings": [true]
				},
				{
					"blockMode": ["askReadout", "ask"],
					"askDenyMode": ["fake"],
					"displayAdvancedSettings": [true]
				}
			]
		},
		{
			"name": "apiWhiteList",
			"displayDependencies": [
				{
					"displayAdvancedSettings": [true]
				}
			]
		},
		"notifications",
		{
			"name": "showNotifications"
		},
		{
			"name": "highlightPageAction",
			"displayDependencies": [
				{
					"showNotifications": [true]
				}
			]
		},
		{
			"name": "displayBadge"
		},
		{
			"name": "highlightBrowserAction"
		},
		{
			"name": "storeImageForInspection",
			"displayDependencies": [
				{
					"showNotifications": [true],
					"displayAdvancedSettings": [true]
				}
			]
		},
		{
			"name": "ignoreList",
			"displayDependencies": [
				{
					"showNotifications": [true],
					"displayAdvancedSettings": [true]
				}
			]
		},
		{
			"name": "showCallingFile",
			"displayDependencies": {
				"blockMode": ["askReadout", "ask"],
				"displayAdvancedSettings": [true]
			}
		},
		{
			"name": "showCompleteCallingStack",
			"displayDependencies": [
				{
					"displayAdvancedSettings": [true]
				}
			]
		},
		"lists",
		{
			"name": "enableStackList",
			"displayDependencies": {
				"blockMode": ["blockReadout", "fakeReadout", "fakeInput", "askReadout", "block", "ask"],
				"displayAdvancedSettings": [true]
			}
		},
		{
			"name": "stackList",
			"displayDependencies": {
				"enableStackList": [true],
				"displayAdvancedSettings": [true]
			}

		},
		{
			"name": "whiteList",
			"displayDependencies": {
				"blockMode": ["blockReadout", "fakeReadout", "fakeInput", "askReadout", "block", "ask"],
				"displayAdvancedSettings": [true]
			}
		},
		{
			"name": "sessionWhiteList",
			"displayDependencies": {
				"blockMode": ["blockReadout", "fakeReadout", "fakeInput", "askReadout", "block", "ask"],
				"displayAdvancedSettings": [true]
			}
		},
		{
			"name": "blackList",
			"displayDependencies": {
				"blockMode": ["blockReadout", "fakeReadout", "fakeInput", "askReadout", "ask", "allow"],
				"displayAdvancedSettings": [true]
			}
		},
		"Audio-API",
		{
			"name": "protectAudio"
		},
		{
			"name": "audioFakeRate",
			"displayDependencies": [
				{
					"blockMode": ["fakeReadout", "fakeInput"],
					"protectAudio": [true],
					"displayAdvancedSettings": [true]
				},
				{
					"blockMode": ["askReadout", "ask", "allow"],
					"askDenyMode": ["fake"],
					"protectAudio": [true],
					"displayAdvancedSettings": [true]
				}
			]
		},
		{
			"name": "audioNoiseLevel",
			"displayDependencies": [
				{
					"blockMode": ["fakeReadout", "fakeInput"],
					"protectAudio": [true],
					"displayAdvancedSettings": [true]
				},
				{
					"blockMode": ["askReadout", "ask", "allow"],
					"askDenyMode": ["fake"],
					"protectAudio": [true],
					"displayAdvancedSettings": [true]
				}
			]
		},
		{
			"name": "useAudioCache",
			"displayDependencies": [
				{
					"blockMode": ["fakeReadout", "fakeInput"],
					"protectAudio": [true],
					"displayAdvancedSettings": [true]
				},
				{
					"blockMode": ["askReadout", "ask", "allow"],
					"askDenyMode": ["fake"],
					"protectAudio": [true],
					"displayAdvancedSettings": [true]
				}
			]
		},
		{
			"name": "audioUseFixedIndices",
			"displayDependencies": [
				{
					"blockMode": ["fakeReadout", "fakeInput"],
					"protectAudio": [true],
					"displayAdvancedSettings": [true]
				},
				{
					"blockMode": ["askReadout", "ask", "allow"],
					"askDenyMode": ["fake"],
					"protectAudio": [true],
					"displayAdvancedSettings": [true]
				}
			]
		},
		{
			"name": "audioFixedIndices",
			"displayDependencies": [
				{
					"audioUseFixedIndices": [true],
					"blockMode": ["fakeReadout", "fakeInput"],
					"protectAudio": [true],
					"displayAdvancedSettings": [true]
				},
				{
					"audioUseFixedIndices": [true],
					"blockMode": ["askReadout", "ask", "allow"],
					"askDenyMode": ["fake"],
					"protectAudio": [true],
					"displayAdvancedSettings": [true]
				}
			]
		},
		"misc",
		{
			"name": "theme"
		},
		{
			"name": "blockDataURLs",
			"displayDependencies": {
				"displayAdvancedSettings": [true]
			}
		},
		{
			"name": "showReleaseNotes"
		},
		{
			"name": "logLevel",
			"displayDependencies": {
				"displayAdvancedSettings": [true]
			}
		},
		"settings",
		{
			"name": "exportSettings",
			"displayDependencies": {
				"displayAdvancedSettings": [true]
			}
		},
		{
			"name": "resetSettings",
			"displayDependencies": {
				"displayAdvancedSettings": [true]
			}
		}
	];
	settingsDisplay.displayHidden = "displayHiddenSettings";

	if ((typeof module) !== "undefined"){
		module.exports = settingsDisplay;
	}
	else {
		window.scope.settingsDisplay = settingsDisplay;
	}
}());<commit_msg>Fixed display dependencies for ignoreList<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* eslint max-lines: off*/
(function(){
	"use strict";
	var settingsDisplay = [
		{
			"name": "displayAdvancedSettings"
		},
		{
			"name": "displayDescriptions"
		},
		{
			"name": "blockMode"
		},
		"asking",
		{
			"name": "askOnlyOnce",
			"displayDependencies": {
				"blockMode": ["askReadout", "ask"]
			}
		},
		{
			"name": "askDenyMode",
			"displayDependencies": {
				"blockMode": ["askReadout", "ask"],
				"displayAdvancedSettings": [true]
			}
		},
		{
			"name": "showCanvasWhileAsking",
			"displayDependencies": {
				"blockMode": ["askReadout", "ask"],
				"displayAdvancedSettings": [true]
			}
		},
		"faking",
		{
			"name": "minFakeSize",
			"displayDependencies": [
				{
					"blockMode": ["fakeReadout", "fakeInput"],
					"displayAdvancedSettings": [true]
				},
				{
					"blockMode": ["askReadout", "ask"],
					"askDenyMode": ["fake"],
					"displayAdvancedSettings": [true]
				}
			]
		},
		{
			"name": "maxFakeSize",
			"displayDependencies": [
				{
					"blockMode": ["fakeReadout", "fakeInput"],
					"displayAdvancedSettings": [true]
				},
				{
					"blockMode": ["askReadout", "ask"],
					"askDenyMode": ["fake"],
					"displayAdvancedSettings": [true]
				}
			]
		},
		{
			"name": "rng",
			"displayDependencies": [
				{
					"blockMode": ["fakeReadout", "fakeInput"],
					"displayAdvancedSettings": [true]
				},
				{
					"blockMode": ["askReadout", "ask"],
					"askDenyMode": ["fake"],
					"displayAdvancedSettings": [true]
				}
			]
		},
		{
			"name": "storePersistentRnd",
			"displayDependencies": [
				{
					"blockMode": ["fakeReadout", "fakeInput"],
					"rng": ["persistent"],
					"displayAdvancedSettings": [true]
				},
				{
					"blockMode": ["askReadout", "ask"],
					"askDenyMode": ["fake"],
					"rng": ["persistent"],
					"displayAdvancedSettings": [true]
				}
			]
		},
		{
			"name": "persistentRndClearInterval",
			"inputs": ["persistentRndClearIntervalValue", "persistentRndClearIntervalUnit"],
			"displayDependencies": [
				{
					"blockMode": ["fakeReadout", "fakeInput"],
					"rng": ["persistent"],
					"displayAdvancedSettings": [true]
				},
				{
					"blockMode": ["askReadout", "ask"],
					"askDenyMode": ["fake"],
					"rng": ["persistent"],
					"displayAdvancedSettings": [true]
				}
			]
		},
		{
			"name": "clearPersistentRnd",
			"displayDependencies": [
				{
					"blockMode": ["fakeReadout", "fakeInput"],
					"rng": ["persistent"],
					"displayAdvancedSettings": [true]
				},
				{
					"blockMode": ["askReadout", "ask"],
					"askDenyMode": ["fake"],
					"rng": ["persistent"],
					"displayAdvancedSettings": [true]
				}
			]
		},
		{
			"name": "ignoreFrequentColors",
			"displayDependencies": [
				{
					"blockMode": ["fakeReadout"],
					"displayAdvancedSettings": [true]
				},
				{
					"blockMode": ["askReadout", "ask"],
					"askDenyMode": ["fake"],
					"displayAdvancedSettings": [true]
				}
			]
		},
		{
			"name": "minColors",
			"displayDependencies": [
				{
					"blockMode": ["fakeReadout"],
					"displayAdvancedSettings": [true]
				},
				{
					"blockMode": ["askReadout", "ask"],
					"askDenyMode": ["fake"],
					"displayAdvancedSettings": [true]
				}
			]
		},
		{
			"name": "fakeAlphaChannel",
			"displayDependencies": [
				{
					"blockMode": ["fakeReadout"],
					"displayAdvancedSettings": [true]
				},
				{
					"blockMode": ["askReadout", "ask"],
					"askDenyMode": ["fake"],
					"displayAdvancedSettings": [true]
				}
			]
		},
		{
			"name": "useCanvasCache",
			"displayDependencies": [
				{
					"blockMode": ["fakeReadout"],
					"displayAdvancedSettings": [true]
				},
				{
					"blockMode": ["askReadout", "ask"],
					"askDenyMode": ["fake"],
					"displayAdvancedSettings": [true]
				}
			]
		},
		{
			"name": "apiWhiteList",
			"displayDependencies": [
				{
					"displayAdvancedSettings": [true]
				}
			]
		},
		"notifications",
		{
			"name": "showNotifications"
		},
		{
			"name": "highlightPageAction",
			"displayDependencies": [
				{
					"showNotifications": [true]
				}
			]
		},
		{
			"name": "displayBadge"
		},
		{
			"name": "highlightBrowserAction"
		},
		{
			"name": "storeImageForInspection",
			"displayDependencies": [
				{
					"showNotifications": [true],
					"displayAdvancedSettings": [true]
				}
			]
		},
		{
			"name": "ignoreList",
			"displayDependencies": [
				{
					"showNotifications": [true],
					"displayAdvancedSettings": [true]
				},
				{
					"displayBadge": [true],
					"displayAdvancedSettings": [true]
				},
				{
					"highlightBrowserAction": ["color", "blink"],
					"displayAdvancedSettings": [true]
				}
			]
		},
		{
			"name": "showCallingFile",
			"displayDependencies": {
				"blockMode": ["askReadout", "ask"],
				"displayAdvancedSettings": [true]
			}
		},
		{
			"name": "showCompleteCallingStack",
			"displayDependencies": [
				{
					"displayAdvancedSettings": [true]
				}
			]
		},
		"lists",
		{
			"name": "enableStackList",
			"displayDependencies": {
				"blockMode": ["blockReadout", "fakeReadout", "fakeInput", "askReadout", "block", "ask"],
				"displayAdvancedSettings": [true]
			}
		},
		{
			"name": "stackList",
			"displayDependencies": {
				"enableStackList": [true],
				"displayAdvancedSettings": [true]
			}

		},
		{
			"name": "whiteList",
			"displayDependencies": {
				"blockMode": ["blockReadout", "fakeReadout", "fakeInput", "askReadout", "block", "ask"],
				"displayAdvancedSettings": [true]
			}
		},
		{
			"name": "sessionWhiteList",
			"displayDependencies": {
				"blockMode": ["blockReadout", "fakeReadout", "fakeInput", "askReadout", "block", "ask"],
				"displayAdvancedSettings": [true]
			}
		},
		{
			"name": "blackList",
			"displayDependencies": {
				"blockMode": ["blockReadout", "fakeReadout", "fakeInput", "askReadout", "ask", "allow"],
				"displayAdvancedSettings": [true]
			}
		},
		"Audio-API",
		{
			"name": "protectAudio"
		},
		{
			"name": "audioFakeRate",
			"displayDependencies": [
				{
					"blockMode": ["fakeReadout", "fakeInput"],
					"protectAudio": [true],
					"displayAdvancedSettings": [true]
				},
				{
					"blockMode": ["askReadout", "ask", "allow"],
					"askDenyMode": ["fake"],
					"protectAudio": [true],
					"displayAdvancedSettings": [true]
				}
			]
		},
		{
			"name": "audioNoiseLevel",
			"displayDependencies": [
				{
					"blockMode": ["fakeReadout", "fakeInput"],
					"protectAudio": [true],
					"displayAdvancedSettings": [true]
				},
				{
					"blockMode": ["askReadout", "ask", "allow"],
					"askDenyMode": ["fake"],
					"protectAudio": [true],
					"displayAdvancedSettings": [true]
				}
			]
		},
		{
			"name": "useAudioCache",
			"displayDependencies": [
				{
					"blockMode": ["fakeReadout", "fakeInput"],
					"protectAudio": [true],
					"displayAdvancedSettings": [true]
				},
				{
					"blockMode": ["askReadout", "ask", "allow"],
					"askDenyMode": ["fake"],
					"protectAudio": [true],
					"displayAdvancedSettings": [true]
				}
			]
		},
		{
			"name": "audioUseFixedIndices",
			"displayDependencies": [
				{
					"blockMode": ["fakeReadout", "fakeInput"],
					"protectAudio": [true],
					"displayAdvancedSettings": [true]
				},
				{
					"blockMode": ["askReadout", "ask", "allow"],
					"askDenyMode": ["fake"],
					"protectAudio": [true],
					"displayAdvancedSettings": [true]
				}
			]
		},
		{
			"name": "audioFixedIndices",
			"displayDependencies": [
				{
					"audioUseFixedIndices": [true],
					"blockMode": ["fakeReadout", "fakeInput"],
					"protectAudio": [true],
					"displayAdvancedSettings": [true]
				},
				{
					"audioUseFixedIndices": [true],
					"blockMode": ["askReadout", "ask", "allow"],
					"askDenyMode": ["fake"],
					"protectAudio": [true],
					"displayAdvancedSettings": [true]
				}
			]
		},
		"misc",
		{
			"name": "theme"
		},
		{
			"name": "blockDataURLs",
			"displayDependencies": {
				"displayAdvancedSettings": [true]
			}
		},
		{
			"name": "showReleaseNotes"
		},
		{
			"name": "logLevel",
			"displayDependencies": {
				"displayAdvancedSettings": [true]
			}
		},
		"settings",
		{
			"name": "exportSettings",
			"displayDependencies": {
				"displayAdvancedSettings": [true]
			}
		},
		{
			"name": "resetSettings",
			"displayDependencies": {
				"displayAdvancedSettings": [true]
			}
		}
	];
	settingsDisplay.displayHidden = "displayHiddenSettings";

	if ((typeof module) !== "undefined"){
		module.exports = settingsDisplay;
	}
	else {
		window.scope.settingsDisplay = settingsDisplay;
	}
}());<|endoftext|>


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

'use strict'

import <%= appNameCamel %> from '../'
import yargs from 'yargs'

const argv = yargs.usage('Usage: $0 (<source path> | <url>) <destination folder>')
  .showHelpOnFail(false, 'Specify --help for available options')
  .version(() => {
    return require('../package').version
  })
  .help('help')
  .argv
<commit_msg>fix(cli): Give execution permission to cli file<commit_after>#!/usr/bin/env node

'use strict'

import <%= appNameCamel %> from '../'
import yargs from 'yargs'

const argv = yargs.usage('Usage: $0 (<source path> | <url>) <destination folder>')
  .showHelpOnFail(false, 'Specify --help for available options')
  .version(() => {
    return require('../package').version
  })
  .help('help')
  .argv
<|endoftext|>


<commit_before>'use strict';

var https = require('https');
var fs = require('fs');

var includes = function (element, searchValue) {
  return Boolean(~element.indexOf(searchValue));
};

var options = {
  hostname: 'developer.mozilla.org',
  port: 443,
  path: '/en-US/docs/Web/CSS$children?expand'
};

var request = https.get(options, function (result) {
  var data = '';
  var cssProperties = [];

  result.setEncoding('utf8');

  result.on('data', function (chunk) {
    data += chunk;
  });

  // Write a filtered array of CSS property names to a JSON file
  result.on('end', function () {
    data = JSON.parse(data);

    data.subpages.forEach(function (element) {
      // Add element if tagged as CSS property and not tagged as Non-standard
      if (includes(element.tags, 'CSS Property') && !includes(element.tags, 'Non-standard')) {
        cssProperties.push(element.title);
      }

      // Get CSS descriptors that are used in at-rules
      if (includes(element.tags, 'At-rule')) {
        element.subpages.forEach(function (subElement) {
          if (includes(subElement.tags, 'CSS Property') && !includes(cssProperties, subElement.title)) {
            cssProperties.push(subElement.title);
          }
        });
      }
    });

    cssProperties.sort();
    cssProperties = JSON.stringify(cssProperties, null, 2);

    fs.writeFile('orders/source.json', cssProperties, function (error) {
      if (error) throw error;
    });
  });
});

request.on('error', function (error) {
  console.error(error);
});
<commit_msg>Refactor property filter code<commit_after>'use strict';

var https = require('https');
var fs = require('fs');

var isStandardProperty = function (tags) {
  return (
    tags.find(function (tagName) {
      return tagName.match(/css property/i);
    }) &&
    !tags.find(function (tagName) {
      return tagName.match(/non-standard/i);
    })
  );
};

var options = {
  hostname: 'developer.mozilla.org',
  port: 443,
  path: '/en-US/docs/Web/CSS$children?expand'
};

var request = https.get(options, function (result) {
  var data = '';

  result.setEncoding('utf8');

  result.on('data', function (chunk) {
    data += chunk;
  });

  // Write a filtered array of CSS property names to a JSON file
  result.on('end', function () {
    data = JSON.parse(data);

    var cssProperties = data.subpages.reduce(function (cssProperties, page) {
      // Add page title if tagged as CSS property and not tagged as Non-standard
      if (isStandardProperty(page.tags)) {
        cssProperties.push(page.title);
      }

      var cssDescriptors = page.subpages.reduce(function (cssDescriptors, subPage) {
        if (isStandardProperty(subPage.tags) && !~cssProperties.indexOf(subPage.title)) {
          cssDescriptors.push(subPage.title)
        }

        return cssDescriptors;
      }, [])

      return [].concat(cssProperties, cssDescriptors);
    }, []);

    cssProperties.sort();
    cssProperties = JSON.stringify(cssProperties, null, 2);

    fs.writeFile('orders/source.json', cssProperties, function (error) {
      if (error) throw error;
    });
  });
});

request.on('error', function (error) {
  console.error(error);
});
<|endoftext|>


<commit_before>const globToRegex = require('glob-to-regexp')
const createFileTypesMapping = require('./createFileTypesMapping')
const defaultFileTypes = require('./defaultFileTypes')
const blockUtils = require('./blockUtils')

exports.createConfig = createConfig
exports.group = group
exports.env = env
exports.match = match

const isFunction = value => typeof value === 'function'

/**
 * Takes an array of webpack blocks and creates a webpack config out of them.
 * Each webpack block is a callback function which will be invoked to return a
 * partial webpack config. These partial configs are merged to create the
 * final, complete webpack config that will be returned.
 *
 * @param {object}     initialContext                 The blueprint for the initial context object.
 * @param {object}     initialContext.webpack         Webpack instance
 * @param {object}     initialContext.webpackVersion  Webpack version (`{ major, minor, ... }`)
 * @param {Function[]} configSetters  Array of functions as returned by webpack blocks.
 * @return {object}                   Webpack config object.
 */
function createConfig (initialContext, configSetters) {
  if (!initialContext) {
    throw new Error(`No initial context passed.`)
  }
  if (!Array.isArray(configSetters) || !configSetters.every(isFunction)) {
    throw new Error(`Expected parameter 'configSetters' to be an array of functions.`)
  }

  /** @deprecated context.fileType */
  const fileType = createFileTypesMapping(defaultFileTypes)
  const context = Object.assign({ fileType }, initialContext)

  const baseConfig = {
    resolve: {
      // Explicitly define default extensions, otherwise blocks will overwrite them instead of extending
      extensions: ['.js', '.json']
    },
    module: {
      rules: []
    },
    plugins: []
  }

  invokePreHooks(configSetters, context)
  const config = invokeConfigSetters(configSetters, context, baseConfig)
  const postProcessedConfig = invokePostHooks(configSetters, context, config)

  return postProcessedConfig
}

/**
 * Applies an array of webpack blocks only if `process.env.NODE_ENV` matches the
 * given `envName`. If no `NODE_ENV` is set, it will be treated as 'development'.
 *
 * @param {string} envName            Environment name like 'development', 'production' or 'testing'.
 * @param {Function[]} configSetters  Array of functions as returned by webpack blocks.
 * @return {Function}
 */
function env (envName, configSetters) {
  const currentEnv = process.env.NODE_ENV || 'development'

  if (currentEnv !== envName) {
    return () => config => config
  } else {
    return group(configSetters)
  }
}

/**
 * Combines an array of blocks to a new joined block. Running this single block
 * has the same effect as running all source blocks.
 *
 * @param {Function[]} configSetters  Array of functions as returned by webpack blocks.
 * @return {Function}
 */
function group (configSetters) {
  const pre = getHooks(configSetters, 'pre')
  const post = getHooks(configSetters, 'post')

  const groupBlock = context => config => invokeConfigSetters(configSetters, context, config)

  return Object.assign(groupBlock, { pre, post })
}

/**
 * State on which files to apply the loader blocks passed in this call. Works
 * like `group()`, but adds the file matching information to the context.
 *
 * @param {string|RegExp|Function|Array} test   A glob like `*.css` or `{*.js, *.jsx}` or something else to use as `loader.test`.
 * @param {object} [options]                    Optional advanced matching options.
 * @param {string|Function|RegExp|Array|object} [options.include]
 * @param {string|Function|RegExp|Array|object} [options.exclude]
 * @param {Function[]} configSetters            Array of functions as returned by webpack blocks.
 * @return {Function}
 */
function match (test, options, configSetters) {
  if (!configSetters && Array.isArray(options)) {
    configSetters = options
    options = {}
  }

  const match = { test: createFileTypeMatcher(test) }

  if (options.exclude) {
    match.exclude = options.exclude
  }
  if (options.include) {
    match.include = options.include
  }

  const groupBlock = context => config => invokeConfigSetters(configSetters, deriveContextWithMatch(context, match), config)

  return Object.assign(groupBlock, {
    pre: context => invokePreHooks(configSetters, deriveContextWithMatch(context, match)),
    post: context => config => invokePostHooks(configSetters, deriveContextWithMatch(context, match), config)
  })
}

function getHooks (configSetters, type) {
  // Get all the blocks' pre/post hooks
  const hooks = configSetters
    .filter(setter => Boolean(setter[type]))
    .map(setter => setter[type])

  // Flatten the array (since each item might be an array as well)
  const flattenedHooks = hooks
    .map(hook => Array.isArray(hook) ? hook : [ hook ])
    .reduce((allHooks, someHooks) => allHooks.concat(someHooks), [])

  return filterDuplicates(flattenedHooks)
}

function invokeConfigSetters (configSetters, context, baseConfig) {
  return configSetters.reduce(
    (config, setter) => {
      const updateFunction = setter(context, blockUtils)

      if (typeof updateFunction !== 'function') {
        throw new Error(
          `Expected a function, instead got a ${typeof updateFunction}. Beware that the block API changed since version 0.x.

          Dump of what should have been a function: ${JSON.stringify(updateFunction)}`
        )
      }

      return updateFunction(config)
    },
    baseConfig
  )
}

function invokePreHooks (configSetters, context) {
  const preHooks = getHooks(configSetters, 'pre')
  preHooks.forEach(hook => hook(context))
}

function invokePostHooks (configSetters, context, config) {
  const postHooks = getHooks(configSetters, 'post')
  return invokeConfigSetters(postHooks, context, config)
}

function createFileTypeMatcher (test) {
  const regexify = glob => globToRegex(glob, { extended: true })

  if (typeof test === 'string') {
    return regexify(test)
  } else if (Array.isArray(test) && test.every(item => typeof item === 'string')) {
    return test.map(item => regexify(item))
  } else {
    return test
  }
}

function deriveContextWithMatch (context, match) {
  // Use an ES Proxy, so the returned context will include `match` and mutations
  // to it will also be applied to the original context. This is important,
  // since we have now got a global context object and local ones.
  return new Proxy(context, {
    has (target, propName) {
      return propName === 'match' ? true : (propName in target)
    },
    get (target, propName) {
      return propName === 'match' ? match : target[propName]
    }
  })
}

function filterDuplicates (array) {
  return array.filter((item, index) => array.indexOf(item) === index)
}
<commit_msg>Better error handling of configSetters param<commit_after>const globToRegex = require('glob-to-regexp')
const createFileTypesMapping = require('./createFileTypesMapping')
const defaultFileTypes = require('./defaultFileTypes')
const blockUtils = require('./blockUtils')

exports.createConfig = createConfig
exports.group = group
exports.env = env
exports.match = match

const isFunction = value => typeof value === 'function'

/**
 * Asserts that given param is an array of functions.
 *
 * @param {Function[]} configSetters Array of functions as returned by webpack blocks.
 */
function assertConfigSetters (configSetters) {
  if (!Array.isArray(configSetters) || !configSetters.every(isFunction)) {
    throw new Error('Expected parameter \'configSetters\' to be an array of functions.')
  }
}

/**
 * Takes an array of webpack blocks and creates a webpack config out of them.
 * Each webpack block is a callback function which will be invoked to return a
 * partial webpack config. These partial configs are merged to create the
 * final, complete webpack config that will be returned.
 *
 * @param {object}     initialContext                 The blueprint for the initial context object.
 * @param {object}     initialContext.webpack         Webpack instance
 * @param {object}     initialContext.webpackVersion  Webpack version (`{ major, minor, ... }`)
 * @param {Function[]} configSetters  Array of functions as returned by webpack blocks.
 * @return {object}                   Webpack config object.
 */
function createConfig (initialContext, configSetters) {
  if (!initialContext) {
    throw new Error(`No initial context passed.`)
  }
  assertConfigSetters(configSetters)

  /** @deprecated context.fileType */
  const fileType = createFileTypesMapping(defaultFileTypes)
  const context = Object.assign({ fileType }, initialContext)

  const baseConfig = {
    resolve: {
      // Explicitly define default extensions, otherwise blocks will overwrite them instead of extending
      extensions: ['.js', '.json']
    },
    module: {
      rules: []
    },
    plugins: []
  }

  invokePreHooks(configSetters, context)
  const config = invokeConfigSetters(configSetters, context, baseConfig)
  const postProcessedConfig = invokePostHooks(configSetters, context, config)

  return postProcessedConfig
}

/**
 * Applies an array of webpack blocks only if `process.env.NODE_ENV` matches the
 * given `envName`. If no `NODE_ENV` is set, it will be treated as 'development'.
 *
 * @param {string} envName            Environment name like 'development', 'production' or 'testing'.
 * @param {Function[]} configSetters  Array of functions as returned by webpack blocks.
 * @return {Function}
 */
function env (envName, configSetters) {
  assertConfigSetters(configSetters)

  const currentEnv = process.env.NODE_ENV || 'development'

  if (currentEnv !== envName) {
    return () => config => config
  } else {
    return group(configSetters)
  }
}

/**
 * Combines an array of blocks to a new joined block. Running this single block
 * has the same effect as running all source blocks.
 *
 * @param {Function[]} configSetters  Array of functions as returned by webpack blocks.
 * @return {Function}
 */
function group (configSetters) {
  assertConfigSetters(configSetters)

  const pre = getHooks(configSetters, 'pre')
  const post = getHooks(configSetters, 'post')

  const groupBlock = context => config => invokeConfigSetters(configSetters, context, config)

  return Object.assign(groupBlock, { pre, post })
}

/**
 * State on which files to apply the loader blocks passed in this call. Works
 * like `group()`, but adds the file matching information to the context.
 *
 * @param {string|RegExp|Function|Array} test   A glob like `*.css` or `{*.js, *.jsx}` or something else to use as `loader.test`.
 * @param {object} [options]                    Optional advanced matching options.
 * @param {string|Function|RegExp|Array|object} [options.include]
 * @param {string|Function|RegExp|Array|object} [options.exclude]
 * @param {Function[]} configSetters            Array of functions as returned by webpack blocks.
 * @return {Function}
 */
function match (test, options, configSetters) {
  if (!configSetters && Array.isArray(options)) {
    configSetters = options
    options = {}
  }

  assertConfigSetters(configSetters)

  const match = { test: createFileTypeMatcher(test) }

  if (options.exclude) {
    match.exclude = options.exclude
  }
  if (options.include) {
    match.include = options.include
  }

  const groupBlock = context => config => invokeConfigSetters(configSetters, deriveContextWithMatch(context, match), config)

  return Object.assign(groupBlock, {
    pre: context => invokePreHooks(configSetters, deriveContextWithMatch(context, match)),
    post: context => config => invokePostHooks(configSetters, deriveContextWithMatch(context, match), config)
  })
}

function getHooks (configSetters, type) {
  // Get all the blocks' pre/post hooks
  const hooks = configSetters
    .filter(setter => Boolean(setter[type]))
    .map(setter => setter[type])

  // Flatten the array (since each item might be an array as well)
  const flattenedHooks = hooks
    .map(hook => Array.isArray(hook) ? hook : [ hook ])
    .reduce((allHooks, someHooks) => allHooks.concat(someHooks), [])

  return filterDuplicates(flattenedHooks)
}

function invokeConfigSetters (configSetters, context, baseConfig) {
  return configSetters.reduce(
    (config, setter) => {
      const updateFunction = setter(context, blockUtils)

      if (typeof updateFunction !== 'function') {
        throw new Error(
          `Expected a function, instead got a ${typeof updateFunction}. Beware that the block API changed since version 0.x.

          Dump of what should have been a function: ${JSON.stringify(updateFunction)}`
        )
      }

      return updateFunction(config)
    },
    baseConfig
  )
}

function invokePreHooks (configSetters, context) {
  const preHooks = getHooks(configSetters, 'pre')
  preHooks.forEach(hook => hook(context))
}

function invokePostHooks (configSetters, context, config) {
  const postHooks = getHooks(configSetters, 'post')
  return invokeConfigSetters(postHooks, context, config)
}

function createFileTypeMatcher (test) {
  const regexify = glob => globToRegex(glob, { extended: true })

  if (typeof test === 'string') {
    return regexify(test)
  } else if (Array.isArray(test) && test.every(item => typeof item === 'string')) {
    return test.map(item => regexify(item))
  } else {
    return test
  }
}

function deriveContextWithMatch (context, match) {
  // Use an ES Proxy, so the returned context will include `match` and mutations
  // to it will also be applied to the original context. This is important,
  // since we have now got a global context object and local ones.
  return new Proxy(context, {
    has (target, propName) {
      return propName === 'match' ? true : (propName in target)
    },
    get (target, propName) {
      return propName === 'match' ? match : target[propName]
    }
  })
}

function filterDuplicates (array) {
  return array.filter((item, index) => array.indexOf(item) === index)
}
<|endoftext|>


<commit_before>//Copyright (c) 2014 Eric Vallee <eric_vallee2003@yahoo.ca>
//MIT License: https://raw.githubusercontent.com/Magnitus-/ExpressSessionMongoDB/master/License.txt

var MongoDB = require('mongodb');
Express = require('express');
Session = require('express-session');
Store = require('../lib/ExpressSessionMongoDB');
Http = require('http');

var RandomIdentifier = 'ExpressSessionMongoDBTestDB'+Math.random().toString(36).slice(-8);
var Context = {};

function Setup(Options, Callback)
{
    MongoDB.MongoClient.connect("mongodb://localhost:27017/"+RandomIdentifier, {native_parser:true}, function(Err, DB) {
        Context['DB'] = DB;
        Store(DB, function(Err, SessionStore) {
            Context['Store'] = SessionStore;
            Options['store'] = SessionStore;
            Context['App'] = Express();
            Context['App'].use(Session(Options));
            //In a real world production case, you'd want to do some sanitization and error checking on URL input
            //As it is, I trust myself to have no malicious intent on my test requests
            Context['App'].post('/:Var/Increment', function(Req, Res) {
                console.log('Req.params.Var: '+Req.params.Var);
                if(Req.session[Req.params.Var])
                {
                    Req.session[Req.params.Var]+=1;
                }
                else
                {
                    Req.session[Req.params.Var]=1;
                }
                Res.end();
            });
            Context['App'].post('/:List/Append/:Var', function(Req, Res) {
                if(!Req.session[Req.params.List])
                {
                    Req.session[Req.params.List]=[];
                }
                Req.session[Req.params.List].push(Req.session[Req.params.Var]);
                Res.end();
            });
            Context['App'].get('/:Var', function(Req, Res) {
                Res.json({'Value': Req.session[Req.params.Var]});
            });
            Context['Server'] = Http.createServer(Context['App']);
            Context['Server'].listen(8080, function() {
                Callback();
            });
        });
    });
}

function TearDown(Callback)
{
    Context['Server'].close(function() {
        Context.DB.dropDatabase(function(Err, Result) {
            Context.DB.close();
            Callback();
        });
    });
}

function GetSessionID(Headers)
{
    if(Headers["set-cookie"])
    {
        var SessionCookie = Headers["set-cookie"][0];
        SessionCookie = SessionCookie.slice(String("connect.sid=").length, SessionCookie.indexOf(';'));
        return(SessionCookie);
    }
    return(null);
}

exports.BasicSetup = {
    'setUp': function(Callback) {
        Setup({'secret': 'qwerty!'}, Callback);
    },
    'tearDown': function(Callback) {
        TearDown(Callback);
    },
    'TestManipulation': function(Test) {
        Test.expect(1);
        var Req = Http.request({'hostname': 'localhost', 'port': 8080, 'method': 'POST', 'path': '/Test/Increment', 'headers': {'Accept': 'application/json'}}, function(Res) {
            Res.on('data', function (Chunk) {
                //Don't really care about this.
            });
            Res.on('end', function () {
                Context['SessionID'] = GetSessionID(Res.headers);
                Req = Http.request({'hostname': 'localhost', 'port': 8080, 'method': 'GET', 'path': '/Test', 'headers': {'Accept': 'application/json', 'cookie': ('connect.sid='+Context['SessionID'])}}, function(Res) {
                    Res.setEncoding('utf8');
                    Res.on('data', function (Chunk) {
                        var Body = JSON.parse(Chunk);
                        Test.ok(Body['Value']==1,'Confirming basic session manipulation works');
                    });
                    Res.on('end', function () {
                        Test.done();
                    });
                });
                Req.end();
            });
        });
        Req.end();
    }
};
<commit_msg>More integration tests completed.<commit_after>//Copyright (c) 2014 Eric Vallee <eric_vallee2003@yahoo.ca>
//MIT License: https://raw.githubusercontent.com/Magnitus-/ExpressSessionMongoDB/master/License.txt

var MongoDB = require('mongodb');
Express = require('express');
Session = require('express-session');
Store = require('../lib/ExpressSessionMongoDB');
Http = require('http');

var RandomIdentifier = 'ExpressSessionMongoDBTestDB'+Math.random().toString(36).slice(-8);
var Context = {};

function Setup(Options, Callback)
{
    MongoDB.MongoClient.connect("mongodb://localhost:27017/"+RandomIdentifier, {native_parser:true}, function(Err, DB) {
        Context['DB'] = DB;
        Store(DB, function(Err, SessionStore) {
            Context['Store'] = SessionStore;
            Options['store'] = SessionStore;
            Context['App'] = Express();
            Context['App'].use(Session(Options));
            //In a real world production case, you'd want to do some sanitization and error checking on URL input
            //As it is, I trust myself to have no malicious intent with regard to my test server
            Context['App'].post('/:Var/Increment', function(Req, Res) {
                if(Req.session[Req.params.Var])
                {
                    Req.session[Req.params.Var]+=1;
                }
                else
                {
                    Req.session[Req.params.Var]=1;
                }
                Res.end();
            });
            Context['App'].post('/:List/Append/:Var', function(Req, Res) {
                if(!Req.session[Req.params.List])
                {
                    Req.session[Req.params.List]=[];
                }
                Req.session[Req.params.List].push(Req.session[Req.params.Var]);
                Res.end();
            });
            Context['App'].get('/:Var', function(Req, Res) {
                Res.json({'Value': Req.session[Req.params.Var]});
            });
            Context['App'].put('/Session/Regeneration', function(Req, Res) {
                Req.session.regenerate(function(Err) {
                    Res.end();
                });
            });
            Context['App'].put('/Session/Destruction', function(Req, Res) {
                Req.session.destroy(function(Err) {
                    Res.end();
                });
            });
            Context['App'].put('/Session/Reload/:Var', function(Req, Res) {
                //<Var> shouldn't be changed.
                //It was put here to enable testing of the desired functionality.
                if(Req.session[Req.params.Var])
                {
                    Req.session[Req.params.Var]+=1;
                }
                else
                {
                    Req.session[Req.params.Var]=1;
                }
                Req.session.reload(function(Err) {
                    Res.end();
                });
            });
            Context['App'].put('/Session/Save/:Var', function(Req, Res) {
                //<Var> should be changed incremented.
                //It was put here to enable testing of the desired functionality.
                if(Req.session[Req.params.Var])
                {
                    Req.session[Req.params.Var]+=1;
                }
                else
                {
                    Req.session[Req.params.Var]=1;
                }
                Req.session.save(function(Err) {
                    Req.session.reload(function(Err) {
                        Res.end();
                    });
                });
            });
            Context['App'].use(function(Err, Req, Res, Next) {
                console.error('Error on test server: '+Err);
            });
            Context['Server'] = Http.createServer(Context['App']);
            Context['Server'].listen(8080, function() {
                Callback();
            });
        });
    });
}

function TearDown(Callback)
{
    Context['Server'].close(function() {
        Context.DB.dropDatabase(function(Err, Result) {
            Context.DB.close();
            Callback();
        });
    });
}

function RequestHandler()
{
    this.SessionID = null;
    if(!RequestHandler.prototype.SetSessionID)
    {
        RequestHandler.prototype.SetSessionID = function(Headers) {
            if(Headers["set-cookie"])
            {
                var SessionCookie = Headers["set-cookie"][0];
                SessionCookie = SessionCookie.slice(String("connect.sid=").length, SessionCookie.indexOf(';'));
                this.SessionID = SessionCookie;
            }
        };

        RequestHandler.prototype.Request = function(Method, Path, GetBody, Callback) {
            var Self = this;
            var RequestObject = {'hostname': 'localhost', 'port': 8080, 'method': Method, 'path': Path, 'headers': {'Accept': 'application/json'}};
            if(this.SessionID)
            {
                RequestObject['headers']['cookie'] = 'connect.sid='+this.SessionID;
            }
            var Req = Http.request(RequestObject, function(Res) {
                Res.setEncoding('utf8');
                var Body = "";
                if(!GetBody)
                {
                    Res.resume();
                }
                else
                {
                    Res.on('data', function (Chunk) {
                        Body+=Chunk;
                    });
                }
                Res.on('end', function() {
                    Self.SetSessionID(Res.headers);
                    Body = GetBody ? JSON.parse(Body) : null;
                    Callback(Body);
                });
            });
            Req.end();
        };
    }
}

exports.BasicSetup = {
    'setUp': function(Callback) {
        Setup({'secret': 'qwerty!'}, Callback);
    },
    'tearDown': function(Callback) {
        TearDown(Callback);
    },
    'TestObjectAPI': function(Test) {
        Test.expect(2);
        var Handler = new RequestHandler();
        Handler.Request('POST', '/Test/Increment', false, function() {
            Handler.Request('GET', '/Test', true, function(Body) {
                Test.ok(Body['Value']==1,'Confirming basic session manipulation works');
                Handler.Request('POST', '/TestArray/Append/Test', false, function() {
                    Handler.Request('POST', '/Test/Increment', false, function() {
                        Handler.Request('POST', '/TestArray/Append/Test', false, function() {
                            Handler.Request('GET', '/TestArray', true, function(Body) {
                                Test.ok(Body['Value'].length && Body['Value'].length ==2 && Body['Value'][0]==1 && Body['Value'][1]==2,'Confirming that session data is preserved and returned over several requests.');
                                Test.done();
                            });
                        });
                    });
                });
            });
        });
    },
    'TestRegenerateMethod': function(Test) {
        Test.expect(2);
        var Handler = new RequestHandler();
        Handler.Request('POST', '/Test/Increment', false, function() {
            Handler.Request('POST', '/Test/Increment', false, function() {
                var PreviousSessionID = Handler.SessionID;
                Handler.Request('PUT', '/Session/Regeneration', false, function() {
                    Test.ok(Handler.SessionID != PreviousSessionID, "Confirming that a new session ID has been generated.");
                    Handler.Request('POST', '/Test/Increment', false, function() {
                        Handler.Request('GET', '/Test', true, function(Body) {
                            Test.ok(Body['Value']==1,'Confirming the session was reset.');
                            Test.done();
                        });
                    });
                });
            });
        });
    },
    'TestDestroyMethod': function(Test) {
        Test.expect(3);
        var Handler = new RequestHandler();
        Handler.Request('POST', '/Test/Increment', false, function() {
            Handler.Request('POST', '/Test/Increment', false, function() {
                var PreviousSessionID = Handler.SessionID;
                Handler.Request('PUT', '/Session/Destruction', false, function() {
                    Test.ok(Handler.SessionID == PreviousSessionID, "Confirming that a new session ID has not been generated.");
                    Handler.Request('POST', '/Test/Increment', false, function() {
                        Test.ok(Handler.SessionID != PreviousSessionID, "Confirming that a new session ID has been generated.");
                        Handler.Request('GET', '/Test', true, function(Body) {
                            Test.ok(Body['Value']==1,'Confirming the session was reset.');
                            Test.done();
                        });
                    });
                });
            });
        });
    },
    'TestReloadMethod': function(Test) {
        Test.expect(1);
        var Handler = new RequestHandler();
        Handler.Request('POST', '/Test/Increment', false, function() {
            Handler.Request('PUT', '/Session/Reload/Test', false, function() {
                Handler.Request('GET', '/Test', true, function(Body) {
                    Test.ok(Body['Value']==1,'Confirming that session was proprerly reloaded.');
                    Test.done();
                });
            });
        });
    },
    'TestSaveMethod': function(Test) {
        Test.expect(1);
        var Handler = new RequestHandler();
        Handler.Request('POST', '/Test/Increment', false, function() {
            Handler.Request('PUT', '/Session/Save/Test', false, function() {
                Handler.Request('GET', '/Test', true, function(Body) {
                    Test.ok(Body['Value']==2,'Confirming that session was proprerly saved.');
                    Test.done();
                });
            });
        });
    }
};
<|endoftext|>


<commit_before>//////////////////////////////////////////////////////////////////////////
// maker
//////////////////////////////////////////////////////////////////////////
//
// Main module
//
/* ----------------------------------------------------------------------
                                                    Object Structures
-------------------------------------------------------------------------

*/
//////////////////////////////////////////////////////////////////////////
// Node.js Exports
exports.createMaker = function( separationString, defaultPropertyValue ) { return new Maker(separationString, defaultPropertyValue); }

//////////////////////////////////////////////////////////////////////////
// Namespace (lol)
var DEBUG = true;

var log = function( text, isImportant ) {
	if(DEBUG && isImportant) {
		console.log("\n******************************************");
		console.log("* " + text);
		console.log("******************************************\n");
	} else if(DEBUG) {
		console.log(text);
	}
};

var pathModule = require("path"),
	basename = require("path").basename,
	traverse = require("traverse"),
	wrench = require("wrench"),
	clone = require("clone"),
	async = require("async"),
	fs = require("fs");

var templateFileExtension = ".tpl";

//////////////////////////////////////////////////////////////////////////
// Constructor
function Maker( separationString, defaultPropertyValue ) {
	this.separationString = separationString || "~~";
	this.templates = {},
	this.defaultPropertyValue = defaultPropertyValue;
} // end Maker()

//////////////////////////////////////////////////////////////////////////
// Returns a template object loaded from a file
Maker.prototype.template = function( templateString, contents ) {
	var templateObj = parseTemplate( templateString, this.separationString );

	for( var property in contents ) {
		if( contents.hasOwnProperty(property) ) {

			templateObj[property] = contents[property] || undefined;
		}
	}

	return templateObj;
} // end template()

//////////////////////////////////////////////////////////////////////////
// Returns an array of the template params within a template string
Maker.prototype.getTemplateParams = function( template, contents ) {
	var templateObj = {};

	if( typeof(template) == "string" ) {
		templateObj = parseTemplate( template, this.separationString );
	} else if( typeof(template) == "object" ) {
		templateObj = template;
	}

	var params = [];

	for( var property in templateObj ) {
		if( templateObj.hasOwnProperty(property) && property != "__fullTemplateString__" && property != "__templateMatches__" ) {
			params.push( property );
		}
	}

	return params;
} // end getTemplateParams()

//////////////////////////////////////////////////////////////////////////
// Retrurns a template object created by processing a file on disk
Maker.prototype.makeTemplate = function( filePath, templateParams ) {
	try {
		var file = fs.readFileSync( filePath, "utf8" );
	} catch( err ) {
		console.log( err );
		return {};
	}

	// Replace all instances of given strings with the given template
	// parameter string within the file
	for( var iParam in templateParams ) {
		if( templateParams.hasOwnProperty(iParam) ) {

			var templateParamString = this.separationString + iParam + this.separationString;
			file = file.replace( new RegExp(templateParams[iParam], "g"), templateParamString );
		}
	}

	var template = parseTemplate( file, this.separationString );

	this.templates[filePath] = template

	return clone( template );
} // end makeTemplate()

//////////////////////////////////////////////////////////////////////////
// Returns a template
Maker.prototype.getTemplate = function( templateName ) {
	return clone( this.templates[templateName] );
} // end getTemplate()

//////////////////////////////////////////////////////////////////////////
// Returns a template filled with the contents given
Maker.prototype.fillTemplate = function( template, contents ) {
	var templateObj = {};

	if( typeof(template) === "string" )
		templateObj = clone( this.templates[template] );
	else
		templateObj = template;

	for( var property in contents ) {
		if( contents.hasOwnProperty(property) ) {

			templateObj[property] = contents[property] || undefined;
		}
	}

	return templateObj;
} // end fillTemplate()

//////////////////////////////////////////////////////////////////////////
// Creates a file on disk composed of the given templates
Maker.prototype.makeFile = function( path, templates, callback ) {
	log( "Making file " + path );

	// If this is a folder, just return
	if( pathModule.extname(path) == "" )
		return;

	// Convert any templates that are still objects into strings
	for( var iTemplate=0; iTemplate<templates.length; ++iTemplate ) {
		if( typeof(templates[iTemplate]) === "object" ) {
			templates[iTemplate] = this.renderTemplateToString( templates[iTemplate] );
		}
	}

	var fileContents = "";

	// Concatenate all template strings into one big string
	for( var iTemplate=0; iTemplate<templates.length; ++iTemplate ) {
		fileContents += templates[iTemplate];
	}

	var folderPath = pathModule.dirname( path );

	// Create the directory if it didn't exist already
	wrench.mkdirSyncRecursive( folderPath );

	// Write the file to disk
	fs.writeFile( path, fileContents, function(err) {
	    if(err) {
	        console.log(err);
	    }

	    if( callback != undefined )
	    	callback( err );
	});
} // end makeFile()

//////////////////////////////////////////////////////////////////////////
// Renders a template recursively back into a string
Maker.prototype.renderTemplateToString = function( template ) {
	var replaceAtIndex = function( string, start, end, replaceString ) {
      return string.substring(0, start) + replaceString + string.substring(end);
	}

	if( typeof(template) === "string" )
		template = this.templates[template];

	// Start with the original template string
	var renderedTemplate = template.__fullTemplateString__,
		matches = template.__templateMatches__;

	for( var iMatch=matches.length-1; iMatch>0; iMatch-= 2 ) {

		var thisTemplateItem = renderedTemplate.substring( matches[iMatch-1] + this.separationString.length, matches[iMatch] );

		var newText = "";

		if( typeof(template[thisTemplateItem]) == "function" )
			newText = template[thisTemplateItem]();
		else if( typeof(template[thisTemplateItem]) == "object" )
			newText = this.renderTemplateToString( template[thisTemplateItem] );
		else
			newText = template[thisTemplateItem];

		renderedTemplate = replaceAtIndex( renderedTemplate, matches[iMatch-1], matches[iMatch]+this.separationString.length, newText );
	}

	return renderedTemplate;
} // end renderTemplate()

//////////////////////////////////////////////////////////////////////////
// Loads all templates inside of a given directory
Maker.prototype.loadTemplateDir = function( templateDir, callback ) {
	var _this = this;
	var templates = {};

	var files = wrench.readdirSyncRecursive( templateDir );

	for( var iFile=0; iFile<files.length; ++iFile ) {
		if( files[iFile].indexOf(".tpl") == -1 )
			continue;

		var path = templateDir + "/" + files[iFile],
			file = fs.readFileSync( path, "utf8" );

		var templateName = basename( path );

		// Trim off the extension
		templateName = templateName.substring( 0, templateName.length - templateFileExtension.length );

		templates[templateName] = _this.template( file );
	}

	log( "Templates loaded from directory " + templateDir );

	_this.templates = templates;

	callback( templates );

} // end loadTemplateDir()

//////////////////////////////////////////////////////////////////////////
// Create templates from all files within a directory, and maps their
// filled out versions to a new location
Maker.prototype.makeTemplatesFromDir = function( source, dest, replacementMap, pathReplacementMap, extensions, contents, callback ) {
	log( "Making directory " + source + " into templates and outputting to " + dest, true );

	dest+= "/";

	// Make the pathReplacementMap, extensions, and contents arguments optional
	if( arguments.length == 4 ) {
		callback = extensions;
		pathReplacementMap = {};
		extensions = [];
		contents = {};
	} else if( arguments.length == 5 ) {
		callback = contents;
		contents = {};
		extensions = [];
	} else if( arguments.length == 6 ) {
		callback = contents;
		contents = {};
	}

	if( extensions.length > 0 ) {
		log( "Filtering files with extensions: " + extensions );
	} else {
		log( "Not filtering for extensions" );
	}

	var _this = this,
		templates = {},
		numTemplates = 0,
		finishedReading = false,
		finishedTemplating = false;

	var files = wrench.readdirSyncRecursive( source );

	// Walk the folder tree recursively\
	if( files != undefined )
		numTemplates += files.length;

	log( numTemplates + " files inspected within directory " + source );
	_this.templates = templates;

	finishedReading = true;

	function iteratorFn( file, finishedCB ) {
		// We don't support templating files with no extension for now
		if( pathModule.extname(file) == "" )
			return finishedCB();

		// Make sure this file has a file extension we care about
		//
		// NOTE: extension filtering is optional, so if extensions is an
		// empty array, any file extension will be let through
		var hasValidExtension = extensions.length > 0 ? false : true;
		for( var iExtension = 0; iExtension < extensions.length; ++iExtension ) {
			var extensionFilter = extensions[iExtension];

			// Figure out whether we're trying to include or exclude this extension
			var isExclude = false;
			if( extensionFilter.charCodeAt(0) == 45 ) {
				isExclude = true;
				extensionFilter = extensionFilter.substring(1);
			}

			if( isExclude ) {
				if( pathModule.extname(file) != extensionFilter )
					hasValidExtension = true;
			} else {
				if( pathModule.extname(file) == extensionFilter )
					hasValidExtension = true;
			}
		}

		// Give up on this round if this file has an invalid extension
		if( !hasValidExtension )
			return finishedCB();

		var path = source + "/" + file;

		var templateObj = _this.makeTemplate( path, replacementMap );

		// Copy over the parameters we're trying to fill in
		for( var property in contents ) {
			if( contents.hasOwnProperty(property) ) {
				templateObj[property] = contents[property] || undefined;
			}
		}

		// Use the path replacement map to modify output locations
		var outputPath = dest + file;
		for( var iItem in pathReplacementMap ) {
			if( pathReplacementMap.hasOwnProperty(iItem) ) {
				outputPath = outputPath.replace( new RegExp(pathReplacementMap[iItem], "g"), iItem );
			}
		}

		_this.makeFile( outputPath, [templateObj], finishedCB );
	}

	function asyncCallback( error, results ) {
		finishedTemplating = true;

		// We have a race condition between reading and templating,
		// so we want to make sure both are finished before calling callback()
		callback();
	}

	async.forEachSeries( files, iteratorFn, asyncCallback );

} // end makeTemplatesFromDir()

//////////////////////////////////////////////////////////////////////////
// Parses a template string and returns an object of its contents
function parseTemplate( templateString, separationString ) {
	var iPosition = 0,
		matches = [],
		templateObj = {};

	iPosition = templateString.indexOf( separationString, iPosition );

	while( iPosition > 0 ) {
		matches.push( iPosition );

		var newPosition = templateString.indexOf( separationString, iPosition+separationString.length );

		iPosition = newPosition;
	}

	if( matches.length % 2 != 0 ) {
		log( "Error while parsing template: Mismatched separation string" );
	}

	// Pull out the name of each template parameter
	for( var iItem = 0; iItem < matches.length; iItem += 2 ) {
		var strItem = templateString.substring( matches[iItem] + separationString.length, matches[iItem+1] );

		// Fill out our object with whatever our default value is
		// We'll fill this template object out with contents later
		templateObj[strItem] = this.defaultPropertyValue;
	}

	// Attach the original template string onto the parsed template
	// object, so that we can reconstruct it later
	templateObj.__fullTemplateString__= templateString;
	templateObj.__templateMatches__ = matches;

	return templateObj;
} // end parseTemplate()
<commit_msg>Fix a few bugs inside of template parsing, and document things a bit<commit_after>//////////////////////////////////////////////////////////////////////////
// maker
//////////////////////////////////////////////////////////////////////////
//
// Main module
//
/* ----------------------------------------------------------------------
                                                    Object Structures
-------------------------------------------------------------------------

*/
//////////////////////////////////////////////////////////////////////////
// Node.js Exports
exports.createMaker = function( separationString, defaultPropertyValue ) { return new Maker(separationString, defaultPropertyValue); }

//////////////////////////////////////////////////////////////////////////
// Namespace (lol)
var DEBUG = true;

var log = function( text, isImportant ) {
	if(DEBUG && isImportant) {
		console.log("\n******************************************");
		console.log("* " + text);
		console.log("******************************************\n");
	} else if(DEBUG) {
		console.log(text);
	}
};

var pathModule = require("path"),
	basename = require("path").basename,
	traverse = require("traverse"),
	wrench = require("wrench"),
	clone = require("clone"),
	async = require("async"),
	fs = require("fs");

var templateFileExtension = ".tpl";

//////////////////////////////////////////////////////////////////////////
// Constructor
function Maker( separationString, defaultPropertyValue ) {
	this.separationString = separationString || "~~";
	this.templates = {},
	this.defaultPropertyValue = defaultPropertyValue;
} // end Maker()

//////////////////////////////////////////////////////////////////////////
// Returns a template object loaded from a file
Maker.prototype.template = function( templateString, contents ) {
	var templateObj = parseTemplate( templateString, this.separationString );

	for( var property in contents ) {
		if( contents.hasOwnProperty(property) ) {

			templateObj[property] = contents[property] || undefined;
		}
	}

	return templateObj;
} // end template()

//////////////////////////////////////////////////////////////////////////
// Returns an array of the template params within a template string
Maker.prototype.getTemplateParams = function( template, contents ) {
	var templateObj = {};

	if( typeof(template) == "string" ) {
		templateObj = parseTemplate( template, this.separationString );
	} else if( typeof(template) == "object" ) {
		templateObj = template;
	}

	var params = [];

	for( var property in templateObj ) {
		if( templateObj.hasOwnProperty(property) && property != "__fullTemplateString__" && property != "__templateMatches__" ) {
			params.push( property );
		}
	}

	return params;
} // end getTemplateParams()

//////////////////////////////////////////////////////////////////////////
// Retrurns a template object created by processing a file on disk
Maker.prototype.makeTemplate = function( filePath, templateParams ) {
	try {
		var file = fs.readFileSync( filePath, "utf8" );
	} catch( err ) {
		console.log( err );
		return {};
	}

	// Replace all instances of given strings with the given template
	// parameter string within the file
	for( var iParam in templateParams ) {
		if( templateParams.hasOwnProperty(iParam) ) {

			var templateParamString = this.separationString + iParam + this.separationString;
			file = file.replace( new RegExp(templateParams[iParam], "g"), templateParamString );
		}
	}

	var template = parseTemplate( file, this.separationString );

	this.templates[filePath] = template

	return clone( template );
} // end makeTemplate()

//////////////////////////////////////////////////////////////////////////
// Returns a template
Maker.prototype.getTemplate = function( templateName ) {
	return clone( this.templates[templateName] );
} // end getTemplate()

//////////////////////////////////////////////////////////////////////////
// Returns a template filled with the contents given
Maker.prototype.fillTemplate = function( template, contents ) {
	var templateObj = {};

	if( typeof(template) === "string" )
		templateObj = clone( this.templates[template] );
	else
		templateObj = template;

	for( var property in contents ) {
		if( contents.hasOwnProperty(property) ) {

			templateObj[property] = contents[property] || undefined;
		}
	}

	return templateObj;
} // end fillTemplate()

//////////////////////////////////////////////////////////////////////////
// Creates a file on disk composed of the given templates
Maker.prototype.makeFile = function( path, templates, callback ) {
	log( "Making file " + path );

	// If this is a folder, just return
	if( pathModule.extname(path) == "" )
		return;

	// Convert any templates that are still objects into strings
	for( var iTemplate=0; iTemplate<templates.length; ++iTemplate ) {
		if( typeof(templates[iTemplate]) === "object" ) {
			templates[iTemplate] = this.renderTemplateToString( templates[iTemplate] );
		}
	}

	var fileContents = "";

	// Concatenate all template strings into one big string
	for( var iTemplate=0; iTemplate<templates.length; ++iTemplate ) {
		fileContents += templates[iTemplate];
	}

	var folderPath = pathModule.dirname( path );

	// Create the directory if it didn't exist already
	wrench.mkdirSyncRecursive( folderPath );

	// Write the file to disk
	fs.writeFile( path, fileContents, function(err) {
	    if(err) {
	        console.log(err);
	    }

	    if( callback != undefined )
	    	callback( err );
	});
} // end makeFile()

//////////////////////////////////////////////////////////////////////////
// Renders a template recursively back into a string
Maker.prototype.renderTemplateToString = function( template ) {
	var replaceAtIndex = function( string, start, end, replaceString ) {
      return string.substring(0, start) + replaceString + string.substring(end);
	}

	if( typeof(template) === "string" )
		template = this.templates[template];

	// Start with the original template string
	var renderedTemplate = template.__fullTemplateString__,
		matches = template.__templateMatches__;

	for( var iMatch=matches.length-1; iMatch>0; iMatch-= 2 ) {

		var thisTemplateItem = renderedTemplate.substring( matches[iMatch-1] + this.separationString.length, matches[iMatch] );

		var newText = "";

		if( typeof(template[thisTemplateItem]) == "function" )
			newText = template[thisTemplateItem]();
		else if( typeof(template[thisTemplateItem]) == "object" )
			newText = this.renderTemplateToString( template[thisTemplateItem] );
		else
			newText = template[thisTemplateItem];

		renderedTemplate = replaceAtIndex( renderedTemplate, matches[iMatch-1], matches[iMatch]+this.separationString.length, newText );
	}

	return renderedTemplate;
} // end renderTemplate()

//////////////////////////////////////////////////////////////////////////
// Loads all templates inside of a given directory
Maker.prototype.loadTemplateDir = function( templateDir, callback ) {
	var _this = this;
	var templates = {};

	var files = wrench.readdirSyncRecursive( templateDir );

	for( var iFile=0; iFile<files.length; ++iFile ) {
		if( files[iFile].indexOf(".tpl") == -1 )
			continue;

		var path = templateDir + "/" + files[iFile],
			file = fs.readFileSync( path, "utf8" );

		var templateName = basename( path );

		// Trim off the extension
		templateName = templateName.substring( 0, templateName.length - templateFileExtension.length );

		templates[templateName] = _this.template( file );
	}

	log( "Templates loaded from directory " + templateDir );

	_this.templates = templates;

	callback( templates );

} // end loadTemplateDir()

//////////////////////////////////////////////////////////////////////////
// Create templates from all files within a directory, and maps their
// filled out versions to a new location
Maker.prototype.makeTemplatesFromDir = function( source, dest, replacementMap, pathReplacementMap, extensions, contents, callback ) {
	log( "Making directory " + source + " into templates and outputting to " + dest, true );

	dest+= "/";

	// Make the pathReplacementMap, extensions, and contents arguments optional
	if( arguments.length == 4 ) {
		callback = extensions;
		pathReplacementMap = {};
		extensions = [];
		contents = {};
	} else if( arguments.length == 5 ) {
		callback = contents;
		contents = {};
		extensions = [];
	} else if( arguments.length == 6 ) {
		callback = contents;
		contents = {};
	}

	if( extensions.length > 0 ) {
		log( "Filtering files with extensions: " + extensions );
	} else {
		log( "Not filtering for extensions" );
	}

	var _this = this,
		templates = {},
		numTemplates = 0,
		finishedReading = false,
		finishedTemplating = false;

	var files = wrench.readdirSyncRecursive( source );

	// Walk the folder tree recursively\
	if( files != undefined )
		numTemplates += files.length;

	log( numTemplates + " files inspected within directory " + source );
	_this.templates = templates;

	finishedReading = true;

	function iteratorFn( file, finishedCB ) {
		// We don't support templating files with no extension for now
		if( pathModule.extname(file) == "" )
			return finishedCB();

		// Make sure this file has a file extension we care about
		//
		// NOTE: extension filtering is optional, so if extensions is an
		// empty array, any file extension will be let through
		var hasValidExtension = extensions.length > 0 ? false : true;
		for( var iExtension = 0; iExtension < extensions.length; ++iExtension ) {
			var extensionFilter = extensions[iExtension];

			// Figure out whether we're trying to include or exclude this extension
			var isExclude = false;
			if( extensionFilter.charCodeAt(0) == 45 ) {
				isExclude = true;
				extensionFilter = extensionFilter.substring(1);
			}

			if( isExclude ) {
				if( pathModule.extname(file) != extensionFilter )
					hasValidExtension = true;
			} else {
				if( pathModule.extname(file) == extensionFilter )
					hasValidExtension = true;
			}
		}

		// Give up on this round if this file has an invalid extension
		if( !hasValidExtension )
			return finishedCB();

		var path = source + "/" + file;

		var templateObj = _this.makeTemplate( path, replacementMap );

		// Copy over the parameters we're trying to fill in
		for( var property in contents ) {
			if( contents.hasOwnProperty(property) ) {
				templateObj[property] = contents[property] || undefined;
			}
		}

		// Use the path replacement map to modify output locations
		var outputPath = dest + file;
		for( var iItem in pathReplacementMap ) {
			if( pathReplacementMap.hasOwnProperty(iItem) ) {
				outputPath = outputPath.replace( new RegExp(pathReplacementMap[iItem], "g"), iItem );
			}
		}

		_this.makeFile( outputPath, [templateObj], finishedCB );
	}

	function asyncCallback( error, results ) {
		finishedTemplating = true;

		// We have a race condition between reading and templating,
		// so we want to make sure both are finished before calling callback()
		callback();
	}

	async.forEachSeries( files, iteratorFn, asyncCallback );

} // end makeTemplatesFromDir()

//////////////////////////////////////////////////////////////////////////
// Parses a template string and returns an object of its contents
function parseTemplate( templateString, separationString ) {
	var iPosition = 0,
		matches = [],
		templateObj = {};

	// Find the first separation string inside the template string
	iPosition = templateString.indexOf( separationString, iPosition );

	while( iPosition > 0 ) {
		// If this is the first of a pair of separation string matches, and we can
		// advance less than the length of the separation string and find a match,
		// advance to that point. This well help avoid this situation:
		// ex: "~~~first~~" will become ~[~~first~~]
		var repeatStringOffset = 0;
		if( matches.length % 2 == 0 ) {
			for( var iTestPos=1; iTestPos<separationString.length; ++iTestPos ) {
				var testPos = templateString.indexOf( separationString, iPosition + iTestPos );

				if( testPos == newPosition + iTestPos )
					repeatStringOffset = iTestPos;
			}
		}

		iPosition+= repeatStringOffset;

		matches.push( iPosition );

		// Move the position by the length of the separation string, because
		// we don't want to cause an error if there are two separation strings
		// in a row, eg:  "~~first~~~~second~~""
		// will become: [~~first~~][~~second~~]
		iPosition+= separationString.length;

		var newPosition = templateString.indexOf( separationString, iPosition );

		iPosition = newPosition;
	}

	if( matches.length % 2 != 0 ) {
		log( "Error while parsing template: Mismatched separation string" );
	}

	// Pull out the name of each template parameter
	for( var iItem = 0; iItem < matches.length; iItem += 2 ) {
		var strItem = templateString.substring( matches[iItem] + separationString.length, matches[iItem+1] );

		// Fill out our object with whatever our default value is
		// We'll fill this template object out with contents later
		templateObj[strItem] = this.defaultPropertyValue;
	}

	// Attach the original template string onto the parsed template
	// object, so that we can reconstruct it later
	templateObj.__fullTemplateString__= templateString;
	templateObj.__templateMatches__ = matches;

	return templateObj;
} // end parseTemplate()
<|endoftext|>


<commit_before>var assert    = require("assert")
  , Sequelize = require("./../../index")
  , sequelize = new Sequelize('database', 'username', 'password')

module.exports = {
  'it should add a new model to the model-manager': function() {
    var s = new Sequelize('database', 'username', 'password')
    assert.eql(s.modelManager.all.length, 0)
    s.define('foo', { title: Sequelize.STRING })
    assert.eql(s.modelManager.all.length, 1)
  },
  'it should handle extended attributes correctly - unique': function() {
    var User = sequelize.define('User' + parseInt(Math.random() * 999999999), {
      username: {type: Sequelize.STRING, unique: true}
    }, { timestamps: false })
    assert.eql(User.attributes, {username:"VARCHAR(255) UNIQUE",id:"INT NOT NULL auto_increment PRIMARY KEY"})
  },
  'it should handle extended attributes correctly - default': function() {
    var User = sequelize.define('User' + parseInt(Math.random() * 999999999), {
      username: {type: Sequelize.STRING, defaultValue: 'foo'}
    }, { timestamps: false })
    assert.eql(User.attributes, {username:"VARCHAR(255) DEFAULT 'foo'",id:"INT NOT NULL auto_increment PRIMARY KEY"})
  },
  'it should handle extended attributes correctly - null': function() {
    var User = sequelize.define('User' + parseInt(Math.random() * 999999999), {
      username: {type: Sequelize.STRING, allowNull: false}
    }, { timestamps: false })
    assert.eql(User.attributes, {username:"VARCHAR(255) NOT NULL",id:"INT NOT NULL auto_increment PRIMARY KEY"})
  },
  'it should handle extended attributes correctly - primary key': function() {
    var User = sequelize.define('User' + parseInt(Math.random() * 999999999), {
      username: {type: Sequelize.STRING, primaryKey: true}
    }, { timestamps: false })
    assert.eql(User.attributes, {username:"VARCHAR(255) PRIMARY KEY",id:"INT NOT NULL auto_increment PRIMARY KEY"})
  },
  'it should add updatedAt and createdAt if timestamps is undefined or true': function() {
    var User1 = sequelize.define('User' + parseInt(Math.random() * 999999999), {})
    var User2 = sequelize.define('User' + parseInt(Math.random() * 999999999), {}, { timestamps: true })

    assert.eql(User1.attributes, {id:"INT NOT NULL auto_increment PRIMARY KEY", updatedAt:"DATETIME NOT NULL", createdAt:"DATETIME NOT NULL"})
    assert.eql(User2.attributes, {id:"INT NOT NULL auto_increment PRIMARY KEY", updatedAt:"DATETIME NOT NULL", createdAt:"DATETIME NOT NULL"})
  },
  'it should add deletedAt if safeDelete is true': function() {
    var User = sequelize.define('User' + parseInt(Math.random() * 999999999), {}, { safeDelete: true })
    assert.eql(User.attributes, {id:"INT NOT NULL auto_increment PRIMARY KEY", deletedAt:"DATETIME", updatedAt:"DATETIME NOT NULL", createdAt:"DATETIME NOT NULL"})
  }
}<commit_msg>also support camelcase as timestamp columns<commit_after>var assert    = require("assert")
  , Sequelize = require("./../../index")
  , sequelize = new Sequelize('database', 'username', 'password')

module.exports = {
  'it should add a new model to the model-manager': function() {
    var s = new Sequelize('database', 'username', 'password')
    assert.eql(s.modelManager.all.length, 0)
    s.define('foo', { title: Sequelize.STRING })
    assert.eql(s.modelManager.all.length, 1)
  },
  'it should handle extended attributes correctly - unique': function() {
    var User = sequelize.define('User' + parseInt(Math.random() * 999999999), {
      username: {type: Sequelize.STRING, unique: true}
    }, { timestamps: false })
    assert.eql(User.attributes, {username:"VARCHAR(255) UNIQUE",id:"INT NOT NULL auto_increment PRIMARY KEY"})
  },
  'it should handle extended attributes correctly - default': function() {
    var User = sequelize.define('User' + parseInt(Math.random() * 999999999), {
      username: {type: Sequelize.STRING, defaultValue: 'foo'}
    }, { timestamps: false })
    assert.eql(User.attributes, {username:"VARCHAR(255) DEFAULT 'foo'",id:"INT NOT NULL auto_increment PRIMARY KEY"})
  },
  'it should handle extended attributes correctly - null': function() {
    var User = sequelize.define('User' + parseInt(Math.random() * 999999999), {
      username: {type: Sequelize.STRING, allowNull: false}
    }, { timestamps: false })
    assert.eql(User.attributes, {username:"VARCHAR(255) NOT NULL",id:"INT NOT NULL auto_increment PRIMARY KEY"})
  },
  'it should handle extended attributes correctly - primary key': function() {
    var User = sequelize.define('User' + parseInt(Math.random() * 999999999), {
      username: {type: Sequelize.STRING, primaryKey: true}
    }, { timestamps: false })
    assert.eql(User.attributes, {username:"VARCHAR(255) PRIMARY KEY",id:"INT NOT NULL auto_increment PRIMARY KEY"})
  },
  'it should add updatedAt and createdAt if timestamps is undefined or true': function() {
    var User1 = sequelize.define('User' + parseInt(Math.random() * 999999999), {})
    var User2 = sequelize.define('User' + parseInt(Math.random() * 999999999), {}, { timestamps: true })

    assert.eql(User1.attributes, {id:"INT NOT NULL auto_increment PRIMARY KEY", updatedAt:"DATETIME NOT NULL", createdAt:"DATETIME NOT NULL"})
    assert.eql(User2.attributes, {id:"INT NOT NULL auto_increment PRIMARY KEY", updatedAt:"DATETIME NOT NULL", createdAt:"DATETIME NOT NULL"})
  },
  'it should add deletedAt if safeDelete is true': function() {
    var User = sequelize.define('User' + parseInt(Math.random() * 999999999), {}, { safeDelete: true })
    assert.eql(User.attributes, {id:"INT NOT NULL auto_increment PRIMARY KEY", deletedAt:"DATETIME", updatedAt:"DATETIME NOT NULL", createdAt:"DATETIME NOT NULL"})
  },
  'timestamp columns should be camelcase if camelcase is passed': function() {
    var User = sequelize.define('User' + parseInt(Math.random() * 999999999), {}, { safeDelete: true, camelcase: true })
    assert.eql(User.attributes, {id:"INT NOT NULL auto_increment PRIMARY KEY", deleted_at:"DATETIME", updated_at:"DATETIME NOT NULL", created_at:"DATETIME NOT NULL"})
  }
}<|endoftext|>