<commit_before>/**
 * Copyright 2015-2016, Google, Inc.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

'use strict';

const gcloud = require('gcloud');
const config = require('../config/config');

const CLOUD_BUCKET = config.get('CLOUD_BUCKET');

const storage = gcloud.storage({
  projectId: config.get('GCLOUD_PROJECT')
});
const bucket = storage.bucket(CLOUD_BUCKET);
const fetch = require('node-fetch');

/**
 * Fetches and Saves an Image to GCloud.
 *
 * @param url - a String with an URL to fetch the image.
 * @param destFile - A string with the name of the Destination file on GCloud.
 * @return a Promise.
 */
function fetchAndSave(url, destFile) {
  return new Promise((resolve, reject) => {
    fetch(url)
      .then(response => {
        return saveImage(response, destFile);
      })
      .then(url => {
        resolve(url);
      })
      .catch(err => {
        reject(err);
      });
  });
}

/**
 * @private
 * Saves the content from the response stream to GCloud.
 *
 * @param response - A Response object returned from fetch.
 * @param destFile - A string with the name of the destination file on GCloud.
 * @return a Promise.
 */
function saveImage(response, destFile) {
  return new Promise((resolve, reject) => {
    if (response.status !== 200) {
      reject(new Error('Bad Response'));
    }

    const contentType = response.headers.get('Content-Type');
    try {
      const file = bucket.file(destFile);

      const writeStream = file.createWriteStream();
      writeStream.on('finish', () => {
        file.setMetadata({
          contentType: contentType
        }, err => {
          if (err) {
            reject(err);
          }
          resolve(getPublicUrl(destFile));
        });
      });
      response.body.pipe(writeStream);
    } catch (e) {
      reject(e);
    }
  });
}

/**
 * @private
 * Given a filename, returns the GCloud address for it.
 *
 * @param filename - a String with the original filename.
 * @return a String containing the GCloud url for the file.
 */
function getPublicUrl(filename) {
  return 'https://storage.googleapis.com/' + CLOUD_BUCKET + '/' + filename;
}

module.exports = {
  fetchAndSave: fetchAndSave
};
<commit_msg>Sets the Content-Type when creating Stream<commit_after>/**
 * Copyright 2015-2016, Google, Inc.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

'use strict';

const gcloud = require('gcloud');
const config = require('../config/config');

const CLOUD_BUCKET = config.get('CLOUD_BUCKET');

const storage = gcloud.storage({
  projectId: config.get('GCLOUD_PROJECT')
});
const bucket = storage.bucket(CLOUD_BUCKET);
const fetch = require('node-fetch');

/**
 * Fetches and Saves an Image to GCloud.
 *
 * @param url - a String with an URL to fetch the image.
 * @param destFile - A string with the name of the Destination file on GCloud.
 * @return a Promise.
 */
function fetchAndSave(url, destFile) {
  return new Promise((resolve, reject) => {
    fetch(url)
      .then(response => {
        return saveImage(response, destFile);
      })
      .then(url => {
        resolve(url);
      })
      .catch(err => {
        reject(err);
      });
  });
}

/**
 * @private
 * Saves the content from the response stream to GCloud.
 *
 * @param response - A Response object returned from fetch.
 * @param destFile - A string with the name of the destination file on GCloud.
 * @return a Promise.
 */
function saveImage(response, destFile) {
  return new Promise((resolve, reject) => {
    if (response.status !== 200) {
      reject(new Error('Bad Response'));
    }

    const contentType = response.headers.get('Content-Type');
    try {
      const file = bucket.file(destFile);

      const writeStream = file.createWriteStream({
        metadata: {
          contentType: contentType
        }
      });

      writeStream.on('error', err => {
        reject(err);
      });

      writeStream.on('finish', () => {
        resolve(getPublicUrl(destFile));
      });

      response.body.pipe(writeStream);
    } catch (e) {
      reject(e);
    }
  });
}

/**
 * @private
 * Given a filename, returns the GCloud address for it.
 *
 * @param filename - a String with the original filename.
 * @return a String containing the GCloud url for the file.
 */
function getPublicUrl(filename) {
  return 'https://storage.googleapis.com/' + CLOUD_BUCKET + '/' + filename;
}

module.exports = {
  fetchAndSave: fetchAndSave
};
<|endoftext|>


<commit_before>// This is the core script of the SkyX engine. It will be served by the extension's main server.
// This architecture will allow us not only to update extensions, but to update the SkyX core remotely.
// Its purposes:
//
// 1. Load all extensions and run them.
// 2. Check for plugin updates from their servers
// 3. Prodive a GeoFS API for adding UI elements and modifying base GeoFS methods.
// 4. Add a UI to manage extensions (add, remove, update, etc.)
//
// (Add if I missed something, Ron)
//
// waits for jquery to load then call method

let remoteContentUrl = "https://rawgit.com/geofs-plugins/plugin-manager-V2/dev/src/";

function waitForJquery(method) {
	if (window.jQuery) {
		method();
	} else {
		setTimeout(function() {
			waitForJquery(method);
		}, 50);
	}
}

// tells the user a message
function notify(msg) {
	// TODO : Implement
	alert("Notifications : " + msg);
}

// debug messages
function debug(msg) {
	console.log(msg);
}

// TODO : Fix this, see github issue
// loads the ui from local storage
// and inserts it into the page
function loadUi() {
		// loading the ui
		var uiData = localStorage.getItem("SkyX/ui.user.html");
		if (uiData === null) {
			notify("Downloading ui, please wait");
		} else {
			// load the ui
			$(".geofs-preference-list").append($("li")
				.addClass("geofs-list-collapsible-item")
				.html(uiData));
		}
}

// TODO : Implement this
// loads the plugins from memory
function loadPlugins() {

}

// TODO : Implement this
// updates the plugins
function updatePlugin() {

}

// TODO : Handle download failure better, see github issue
// updates core.user.js and saves it into localStorage
function updateSelf() {

	// updating files
	$.ajax({
		url: "https://api.github.com/repos/geofs-plugins/plugin-manager-V2/commits/dev",
		success: function(data) {
			debug("Succesfuly got latest commit hash");
			let latestRemoteCommitHash = data["sha"];

			if (latestRemoteCommitHash != localStorage.getItem("SkyX/version")) {

				let filesToUpdate = ["core.user.js", "ui.user.html"];

				// if one of the updates failed then delete everything
				// and hope for the best
				let hasFailed = false;
				let filesFinished = 0;

				// wait for the files to finish update and notify
				// the user accordingly
				function waitForUpdate() {
					console.log("Testing stuff out");
					if (filesFinished == filesToUpdate.length) {
						if (hasFailed) {
							notify("One of the files failed to download");

							// deleting all of the files
							for (var file in filesToUpdate) {
								localStorage.setItem("SkyX/" + filesToUpdate[file], null);
							}
						} else {
							localStorage.setItem("SkyX/version", latestRemoteCommitHash);
							notify("Succesfuly updated SkyX V2, please refresh GeoFS for the changes to take affect");
						}
					} else {
						setTimeout(function() {
							waitForUpdate();
						}, 50);
					}
				}

				waitForUpdate();

				// go over all of the files and update them all
				for (let file in filesToUpdate) {
					$.ajax({
						url: remoteContentUrl + filesToUpdate[file],
						success: function(data) {
							debug("got " + filesToUpdate[file]);
							if ((!("SkyX/ui.user.html" in localStorage)) && filesToUpdate[file] == "ui.user.html") {
								$(".geofs-list").append($("li")
									.addClass("geofs-list-collapsible-item")
									.html(uiData));
							}

							localStorage.setItem("SkyX/" + filesToUpdate[file], data);
							filesFinished++;
						},

						error: function() {
							debug("failed to download" + filesToUpdate[file]);
							hasFailed = true;
							filesFinished++;
						}
					});
				}
			} else {
				debug("Everything is up to date");
			}
		},
		error: function() {
			debug("Getting latest commit hash failed");
		}
	});
}

// main function
// incharge of updating and loading the appropriate files
function main() {

	//if this browser suppoprt Local Storage
	if (typeof(Storage) !== "undefined") {

		loadUi();

		loadPlugins();

		updatePlugin();

		updateSelf();

	} else {
		notify("ERROR , your browser doesn't support Local Storage");
	}
}

// waits for jQuery to load and then calls
waitForJquery(main);
<commit_msg>Typos, typos everywhere<commit_after>// This is the core script of the SkyX engine. It will be served by the extension's main server.
// This architecture will allow us not only to update extensions, but to update the SkyX core remotely.
// Its purposes:
//
// 1. Load all extensions and run them.
// 2. Check for plugin updates from their servers
// 3. Prodive a GeoFS API for adding UI elements and modifying base GeoFS methods.
// 4. Add a UI to manage extensions (add, remove, update, etc.)
//
// (Add if I missed something, Ron)
//
// waits for jquery to load then call method

let remoteContentUrl = "https://rawgit.com/geofs-plugins/plugin-manager-V2/dev/src/";

function waitForJquery(method) {
	if (window.jQuery) {
		method();
	} else {
		setTimeout(function() {
			waitForJquery(method);
		}, 50);
	}
}

// tells the user a message
function notify(msg) {
	// TODO : Implement
	alert("Notifications : " + msg);
}

// debug messages
function debug(msg) {
	console.log(msg);
}

// TODO : Fix this, see github issue
// loads the ui from local storage
// and inserts it into the page
function loadUi() {
		// loading the ui
		var uiData = localStorage.getItem("SkyX/ui.user.html");
		if (uiData === null) {
			notify("Downloading ui, please wait");
		} else {
			// load the ui
			$(".geofs-preference-list").append($("li")
				.addClass("geofs-list-collapsible-item")
				.html(uiData));
		}
}

// TODO : Implement this
// loads the plugins from memory
function loadPlugins() {

}

// TODO : Implement this
// updates the plugins
function updatePlugin() {

}

// TODO : Handle download failure better, see github issue
// updates core.user.js and saves it into localStorage
function updateSelf() {

	// updating files
	$.ajax({
		url: "https://api.github.com/repos/geofs-plugins/plugin-manager-V2/commits/dev",
		success: function(data) {
			debug("Succesfuly got latest commit hash");
			let latestRemoteCommitHash = data["sha"];

			if (latestRemoteCommitHash != localStorage.getItem("SkyX/version")) {

				let filesToUpdate = ["core.user.js", "ui.user.html"];

				// if one of the updates failed then delete everything
				// and hope for the best
				let hasFailed = false;
				let filesFinished = 0;

				// wait for the files to finish update and notify
				// the user accordingly
				function waitForUpdate() {
					(filesFinished == filesToUpdate.length) {
						if (hasFailed) {
							notify("One of the files failed to download");

							// deleting all of the files
							for (var file in filesToUpdate) {
								localStorage.setItem("SkyX/" + filesToUpdate[file], null);
							}
						} else {
							localStorage.setItem("SkyX/version", latestRemoteCommitHash);
							notify("Succesfuly updated SkyX V2, please refresh GeoFS for the changes to take affect");
						}
					} else {
						setTimeout(function() {
							waitForUpdate();
						}, 50);
					}
				}

				waitForUpdate();

				// go over all of the files and update them all
				for (let file in filesToUpdate) {
					$.ajax({
						url: remoteContentUrl + filesToUpdate[file],
						success: function(data) {
							debug("got " + filesToUpdate[file]);
							if ((!("SkyX/ui.user.html" in localStorage)) && filesToUpdate[file] == "ui.user.html") {
								$(".geofs-list").append($("li")
									.addClass("geofs-list-collapsible-item")
									.html(data));
							}

							localStorage.setItem("SkyX/" + filesToUpdate[file], data);
							filesFinished++;
						},

						error: function() {
							debug("failed to download" + filesToUpdate[file]);
							hasFailed = true;
							filesFinished++;
						}
					});
				}
			} else {
				debug("Everything is up to date");
			}
		},
		error: function() {
			debug("Getting latest commit hash failed");
		}
	});
}

// main function
// incharge of updating and loading the appropriate files
function main() {

	//if this browser suppoprt Local Storage
	if (typeof(Storage) !== "undefined") {

		loadUi();

		loadPlugins();

		updatePlugin();

		updateSelf();

	} else {
		notify("ERROR , your browser doesn't support Local Storage");
	}
}

// waits for jQuery to load and then calls
waitForJquery(main);
<|endoftext|>


<commit_before>const API_EVENTS_URL = '/api/v1/splash-events/?start_time__gte=2017-08-01';
const API_SLACK_URL = '/api/v1/slack/';

export { API_EVENTS_URL, API_SLACK_URL };
<commit_msg>update start date<commit_after>const API_EVENTS_URL = '/api/v1/splash-events/?start_time__gte=2018-08-01';
const API_SLACK_URL = '/api/v1/slack/';

export { API_EVENTS_URL, API_SLACK_URL };
<|endoftext|>


<commit_before>/* Filemanager pattern.
 *
 * Options:
 *    aceConfig(object): ace configuration ({})
 *    actionUrl(string): base url to get/put data. Action is passed is an a parameters, ?action=(dataTree, newFile, deleteFile, getFile, saveFile)
 *    uploadUrl(string): url to upload files to
 *    resourceSearchUrl(string): url to search for resources to customize
 *    translations(object): mapping of translation strings
 *
 * Documentation:
 *
 *
 *   {{ example-1 }}
 *
 *   Example with upload
 *
 *   {{ example-2 }}
 *
 * Example: example-1
 *

 *

 *
 * Example: example-2
 *

 *

 *
 */

define([
  'jquery',
  'mockup-patterns-base',
  'underscore',
  'backbone',
  'mockup-ui-url/views/base',
  'mockup-patterns-tree',
  'mockup-patterns-texteditor',
  'text!mockup-patterns-filemanager-url/templates/app.xml',
  'mockup-ui-url/views/toolbar',
  'mockup-ui-url/views/button',
  'mockup-ui-url/views/buttongroup',
  'mockup-patterns-filemanager-url/js/addnew',
  'mockup-patterns-filemanager-url/js/newfolder',
  'mockup-patterns-filemanager-url/js/delete',
  'mockup-patterns-filemanager-url/js/customize',
  'mockup-patterns-filemanager-url/js/rename',
  'mockup-patterns-filemanager-url/js/upload',
  'mockup-utils',
  'text!mockup-ui-url/templates/popover.xml'
], function($, Base, _, Backbone, BaseView, Tree, TextEditor, AppTemplate, Toolbar,
            ButtonView, ButtonGroup, AddNewView, NewFolderView, DeleteView,
            CustomizeView, RenameView, UploadView, utils) {
  'use strict';

  var FileManager = Base.extend({
    name: 'filemanager',
    template: _.template(AppTemplate),
    tabItemTemplate: _.template(
      '<li class="active" data-path="<%= path %>">' +
        '<%= path %>' +
        '' +
          '' +
        '' +
      '
'),
    saveBtn: null,
    fileData: {},  /* mapping of files to data that it describes */
    defaults: {
      aceConfig: {},
      actionUrl: null,
      uploadUrl: null,
      resourceSearchUrl: null,
      translations: {
        add_new_file: 'New file',
        add_new_file_tooltip: 'Add new file to current folder',
        add_override: 'Add new override',
        add_override_tooltip: 'Find resource in plone to override',
        delete: 'Delete',
        delete_tooltip: 'Delete currently selected file',
        new_folder: 'New folder',
        new_folder_tooltip: 'Add new folder to current directory',
        rename: 'Rename',
        rename_tooltip: 'Rename currently selected resource',
        upload: 'Upload',
        upload_tooltip: 'Upload file to current directory',
        filename: 'Filename',
        enter_filename: 'Enter filename',
        add: 'Add',
        search: 'Search',
        search_resources: 'Search resources',
        customize: 'Customize',
        yes_delete: 'Yes, delete',
        delete_question: 'Are you sure you want to delete this resource?',
        folder_name: 'Folder name',
        enter_folder_name: 'Enter folder name',
        save: 'Save'
      },
      treeConfig: {
        autoOpen: true
      }
    },
    init: function() {
      var self = this;
      if (self.options.actionUrl === null) {
        self.$el.html('Must specify actionUrl setting for pattern');
        return;
      }
      self.options.treeConfig = $.extend(true, {}, self.treeConfig, {
        dataUrl: self.options.actionUrl + '?action=dataTree'
      });

      var translations = self.options.translations;

      self.fileData = {};
      self.currentPath;

      self.saveBtn = new ButtonView({
        id: 'save',
        title: translations.save,
        context: 'success'
      });

      var newFolderView = new NewFolderView({
        triggerView: new ButtonView({
          id: 'newfolder',
          title: translations.new_folder,
          tooltip: translations.new_folder_tooltip,
          context: 'default'
        }),
        app: self
      });
      var addNewView = new AddNewView({
        triggerView: new ButtonView({
          id: 'addnew',
          title: translations.add_new_file,
          tooltip: translations.add_new_file_tooltip,
          context: 'default'
        }),
        app: self
      });
      var renameView = new RenameView({
        triggerView: new ButtonView({
          id: 'rename',
          title: translations.rename,
          tooltip: translations.rename_tooltip,
          context: 'default'
        }),
        app: self
      });
      var deleteView = new DeleteView({
        triggerView: new ButtonView({
          id: 'delete',
          title: translations.delete,
          tooltip: translations.delete_tooltip,
          context: 'danger'
        }),
        app: self
      });

      self.views = [
        newFolderView,
        addNewView,
        renameView,
        deleteView
      ];
      var mainButtons = [
        newFolderView.triggerView,
        addNewView.triggerView
      ];

      if (self.options.uploadUrl){
        var uploadView = new UploadView({
          triggerView: new ButtonView({
            id: 'upload',
            title: translations.upload,
            tooltip: translations.upload_tooltip,
            context: 'default'
          }),
          app: self
        });
        self.views.push(uploadView);
        mainButtons.push(uploadView.triggerView);
      }
      if (self.options.resourceSearchUrl){
        var customizeView = new CustomizeView({
          triggerView: new ButtonView({
            id: 'customize',
            title: translations.add_override,
            tooltip: translations.add_override_tooltip,
            context: 'default'
          }),
          app: self
        });
        self.views.push(customizeView);
        mainButtons.push(customizeView.triggerView);
      }

      self.toolbar = new Toolbar({
        items: [
          new ButtonGroup({
            items: mainButtons,
            id: 'main',
            app: self
          }),
          new ButtonGroup({
            items: [
              renameView.triggerView,
              deleteView.triggerView
            ],
            id: 'secondary',
            app: self
          }),
          self.saveBtn
        ]
      });

      self.saveBtn.on('button:click', function(e) {
        self.doAction('saveFile', {
          type: 'POST',
          data: {
            path: self.getNodePath(),
            data: self.ace.editor.getValue(),
            _authenticator: utils.getAuthenticator()
          },
          success: function(data) {
            /* XXX unhighlight save button */
          }
        });
      });
      self.render();
    },
    $: function(selector){
      return this.$el.find(selector);
    },
    render: function(){
      var self = this;
      self.$el.html(self.template(self.options));
      self.$('#toolbar').append(self.toolbar.render().el);
      _.each(self.views, function(view) {
        self.$('#toolbar').append(view.render().el);
      });
      self.$tree = self.$('.tree');
      self.$nav = self.$('nav');
      self.$tabs = $('ul.nav', self.$nav);
      self.options.treeConfig.onLoad = function(tree) {
        // on loading initial data, activate first node if available
        var node = self.$tree.tree('getNodeById', 1);
        if (node){
          self.$tree.tree('selectNode', node);
          self.openFile({node: node});
        }
      };
      self.tree = new Tree(self.$tree, self.options.treeConfig);
      self.$tree.bind('tree.click', function(e) {
        self.openFile(e);
      });
      self.$editor = self.$('.editor');
    },
    openFile: function(event) {
      var self = this;
      var doc = event.node.name;
      if (event.node.folder){
        return true;
      }
      if(self.fileData[doc]) {
        $('li', self.$tabs).removeClass('active');
        var $existing = $('[data-path="' + doc + '"]');
        if ($existing.length === 0){
          var $item = $(self.tabItemTemplate({path: doc}));
          self.$tabs.append($item);
          $('.remove', $item).click(function(e){
            e.preventDefault();
            if ($(this).parent().hasClass('active'))
            {
              var $siblings = $(this).parent().siblings();
              if ($siblings.length > 0){
                var $item;
                if ($(this).parent().prev().length > 0){
                  $item = $(this).parent().prev();
                } else {
                  $item = $(this).parent().next();
                }
                $item.addClass('active');
                self.openEditor($item.attr('data-path'));
              } else {
                self.ace.setText('');
              }
            }
            $(this).parent().remove();
          });
          $('.select', $item).click(function(e){
            e.preventDefault();
            $('li', self.$tabs).removeClass('active');
            var $li = $(this).parent();
            $li.addClass('active');
            self.$tree.tree('selectNode', event.node);
            self.openFile({node: event.node});
          });
        }else{
          $existing.addClass('active');
        }
        self.openEditor(doc);
      } else {
        self.doAction('getFile', {
          data: { path: doc },
          dataType: 'json',
          success: function(data) {
            self.fileData[doc] = data;
            self.openFile(event);
          }
        });
      }
    },
    doAction: function(action, options) {
      var self = this;
      if (!options){
        options = {};
      }
      $.ajax({
        url: self.options.actionUrl,
        type: options.type || 'GET',
        data: $.extend({}, {
          _authenticator: utils.getAuthenticator(),
          action: action
        }, options.data || {}),
        success: options.success,
        failure: options.failure || function() {}
      });
    },
    openEditor: function(path) {
      var self = this;
      // first we need to save the current editor content
      if(self.currentPath) {
        self.fileData[self.currentPath].contents = self.ace.editor.getValue();
      }
      self.currentPath = path;
      if (self.ace !== undefined){
        self.ace.editor.destroy();
      }
      self.ace = new TextEditor(self.$editor, {
        width: self.$editor.width()
      });
      self.ace.setSyntax(path);
      self.ace.setText(self.fileData[path].contents);
      self.ace.editor.clearSelection();
    },
    getSelectedNode: function() {
      return this.$tree.tree('getSelectedNode');
    },
    getNodePath: function(node) {
      var self = this;
      if(node === undefined){
        node = self.getSelectedNode();
      }
      var path = self.getFolderPath(node.parent);
      if (path !== '/'){
        path += '/';
      }
      return path + node.name;
    },
    getFolderPath: function(node){
      var self = this;
      if(node === undefined){
        node = self.getSelectedNode();
      }
      var parts = [];
      if (!node.folder && node.name){
        node = node.parent;
      }
      while (node.name){
        parts.push(node.name);
        node = node.parent;
      }
      return '/' + parts.join('/');
    }

  });

  return FileManager;

});
<commit_msg>fix openfile if file not in root<commit_after>/* Filemanager pattern.
 *
 * Options:
 *    aceConfig(object): ace configuration ({})
 *    actionUrl(string): base url to get/put data. Action is passed is an a parameters, ?action=(dataTree, newFile, deleteFile, getFile, saveFile)
 *    uploadUrl(string): url to upload files to
 *    resourceSearchUrl(string): url to search for resources to customize
 *    translations(object): mapping of translation strings
 *
 * Documentation:
 *
 *
 *   {{ example-1 }}
 *
 *   Example with upload
 *
 *   {{ example-2 }}
 *
 * Example: example-1
 *

 *

 *
 * Example: example-2
 *

 *

 *
 */

define([
  'jquery',
  'mockup-patterns-base',
  'underscore',
  'backbone',
  'mockup-ui-url/views/base',
  'mockup-patterns-tree',
  'mockup-patterns-texteditor',
  'text!mockup-patterns-filemanager-url/templates/app.xml',
  'mockup-ui-url/views/toolbar',
  'mockup-ui-url/views/button',
  'mockup-ui-url/views/buttongroup',
  'mockup-patterns-filemanager-url/js/addnew',
  'mockup-patterns-filemanager-url/js/newfolder',
  'mockup-patterns-filemanager-url/js/delete',
  'mockup-patterns-filemanager-url/js/customize',
  'mockup-patterns-filemanager-url/js/rename',
  'mockup-patterns-filemanager-url/js/upload',
  'mockup-utils',
  'text!mockup-ui-url/templates/popover.xml'
], function($, Base, _, Backbone, BaseView, Tree, TextEditor, AppTemplate, Toolbar,
            ButtonView, ButtonGroup, AddNewView, NewFolderView, DeleteView,
            CustomizeView, RenameView, UploadView, utils) {
  'use strict';

  var FileManager = Base.extend({
    name: 'filemanager',
    template: _.template(AppTemplate),
    tabItemTemplate: _.template(
      '<li class="active" data-path="<%= path %>">' +
        '<%= path %>' +
        '' +
          '' +
        '' +
      '
'),
    saveBtn: null,
    fileData: {},  /* mapping of files to data that it describes */
    defaults: {
      aceConfig: {},
      actionUrl: null,
      uploadUrl: null,
      resourceSearchUrl: null,
      translations: {
        add_new_file: 'New file',
        add_new_file_tooltip: 'Add new file to current folder',
        add_override: 'Add new override',
        add_override_tooltip: 'Find resource in plone to override',
        delete: 'Delete',
        delete_tooltip: 'Delete currently selected file',
        new_folder: 'New folder',
        new_folder_tooltip: 'Add new folder to current directory',
        rename: 'Rename',
        rename_tooltip: 'Rename currently selected resource',
        upload: 'Upload',
        upload_tooltip: 'Upload file to current directory',
        filename: 'Filename',
        enter_filename: 'Enter filename',
        add: 'Add',
        search: 'Search',
        search_resources: 'Search resources',
        customize: 'Customize',
        yes_delete: 'Yes, delete',
        delete_question: 'Are you sure you want to delete this resource?',
        folder_name: 'Folder name',
        enter_folder_name: 'Enter folder name',
        save: 'Save'
      },
      treeConfig: {
        autoOpen: true
      }
    },
    init: function() {
      var self = this;
      if (self.options.actionUrl === null) {
        self.$el.html('Must specify actionUrl setting for pattern');
        return;
      }
      self.options.treeConfig = $.extend(true, {}, self.treeConfig, {
        dataUrl: self.options.actionUrl + '?action=dataTree'
      });

      var translations = self.options.translations;

      self.fileData = {};
      self.currentPath;

      self.saveBtn = new ButtonView({
        id: 'save',
        title: translations.save,
        context: 'success'
      });

      var newFolderView = new NewFolderView({
        triggerView: new ButtonView({
          id: 'newfolder',
          title: translations.new_folder,
          tooltip: translations.new_folder_tooltip,
          context: 'default'
        }),
        app: self
      });
      var addNewView = new AddNewView({
        triggerView: new ButtonView({
          id: 'addnew',
          title: translations.add_new_file,
          tooltip: translations.add_new_file_tooltip,
          context: 'default'
        }),
        app: self
      });
      var renameView = new RenameView({
        triggerView: new ButtonView({
          id: 'rename',
          title: translations.rename,
          tooltip: translations.rename_tooltip,
          context: 'default'
        }),
        app: self
      });
      var deleteView = new DeleteView({
        triggerView: new ButtonView({
          id: 'delete',
          title: translations.delete,
          tooltip: translations.delete_tooltip,
          context: 'danger'
        }),
        app: self
      });

      self.views = [
        newFolderView,
        addNewView,
        renameView,
        deleteView
      ];
      var mainButtons = [
        newFolderView.triggerView,
        addNewView.triggerView
      ];

      if (self.options.uploadUrl){
        var uploadView = new UploadView({
          triggerView: new ButtonView({
            id: 'upload',
            title: translations.upload,
            tooltip: translations.upload_tooltip,
            context: 'default'
          }),
          app: self
        });
        self.views.push(uploadView);
        mainButtons.push(uploadView.triggerView);
      }
      if (self.options.resourceSearchUrl){
        var customizeView = new CustomizeView({
          triggerView: new ButtonView({
            id: 'customize',
            title: translations.add_override,
            tooltip: translations.add_override_tooltip,
            context: 'default'
          }),
          app: self
        });
        self.views.push(customizeView);
        mainButtons.push(customizeView.triggerView);
      }

      self.toolbar = new Toolbar({
        items: [
          new ButtonGroup({
            items: mainButtons,
            id: 'main',
            app: self
          }),
          new ButtonGroup({
            items: [
              renameView.triggerView,
              deleteView.triggerView
            ],
            id: 'secondary',
            app: self
          }),
          self.saveBtn
        ]
      });

      self.saveBtn.on('button:click', function(e) {
        self.doAction('saveFile', {
          type: 'POST',
          data: {
            path: self.getNodePath(),
            data: self.ace.editor.getValue(),
            _authenticator: utils.getAuthenticator()
          },
          success: function(data) {
            /* XXX unhighlight save button */
          }
        });
      });
      self.render();
    },
    $: function(selector){
      return this.$el.find(selector);
    },
    render: function(){
      var self = this;
      self.$el.html(self.template(self.options));
      self.$('#toolbar').append(self.toolbar.render().el);
      _.each(self.views, function(view) {
        self.$('#toolbar').append(view.render().el);
      });
      self.$tree = self.$('.tree');
      self.$nav = self.$('nav');
      self.$tabs = $('ul.nav', self.$nav);
      self.options.treeConfig.onLoad = function(tree) {
        // on loading initial data, activate first node if available
        var node = self.$tree.tree('getNodeById', 1);
        if (node){
          self.$tree.tree('selectNode', node);
          self.openFile({node: node});
        }
      };
      self.tree = new Tree(self.$tree, self.options.treeConfig);
      self.$tree.bind('tree.click', function(e) {
        self.openFile(e);
      });
      self.$editor = self.$('.editor');
    },
    openFile: function(event) {
      var self = this;
      var doc = event.node.path;
      if (event.node.folder){
        return true;
      }
      if(self.fileData[doc]) {
        $('li', self.$tabs).removeClass('active');
        var $existing = $('[data-path="' + doc + '"]');
        if ($existing.length === 0){
          var $item = $(self.tabItemTemplate({path: doc}));
          self.$tabs.append($item);
          $('.remove', $item).click(function(e){
            e.preventDefault();
            if ($(this).parent().hasClass('active'))
            {
              var $siblings = $(this).parent().siblings();
              if ($siblings.length > 0){
                var $item;
                if ($(this).parent().prev().length > 0){
                  $item = $(this).parent().prev();
                } else {
                  $item = $(this).parent().next();
                }
                $item.addClass('active');
                self.openEditor($item.attr('data-path'));
              } else {
                self.ace.setText('');
              }
            }
            $(this).parent().remove();
          });
          $('.select', $item).click(function(e){
            e.preventDefault();
            $('li', self.$tabs).removeClass('active');
            var $li = $(this).parent();
            $li.addClass('active');
            self.$tree.tree('selectNode', event.node);
            self.openFile({node: event.node});
          });
        }else{
          $existing.addClass('active');
        }
        self.openEditor(doc);
      } else {
        self.doAction('getFile', {
          data: { path: doc },
          dataType: 'json',
          success: function(data) {
            self.fileData[doc] = data;
            self.openFile(event);
          }
        });
      }
    },
    doAction: function(action, options) {
      var self = this;
      if (!options){
        options = {};
      }
      $.ajax({
        url: self.options.actionUrl,
        type: options.type || 'GET',
        data: $.extend({}, {
          _authenticator: utils.getAuthenticator(),
          action: action
        }, options.data || {}),
        success: options.success,
        failure: options.failure || function() {}
      });
    },
    openEditor: function(path) {
      var self = this;
      // first we need to save the current editor content
      if(self.currentPath) {
        self.fileData[self.currentPath].contents = self.ace.editor.getValue();
      }
      self.currentPath = path;
      if (self.ace !== undefined){
        self.ace.editor.destroy();
      }
      self.ace = new TextEditor(self.$editor, {
        width: self.$editor.width()
      });
      self.ace.setSyntax(path);
      self.ace.setText(self.fileData[path].contents);
      self.ace.editor.clearSelection();
    },
    getSelectedNode: function() {
      return this.$tree.tree('getSelectedNode');
    },
    getNodePath: function(node) {
      var self = this;
      if(node === undefined){
        node = self.getSelectedNode();
      }
      var path = self.getFolderPath(node.parent);
      if (path !== '/'){
        path += '/';
      }
      return path + node.name;
    },
    getFolderPath: function(node){
      var self = this;
      if(node === undefined){
        node = self.getSelectedNode();
      }
      var parts = [];
      if (!node.folder && node.name){
        node = node.parent;
      }
      while (node.name){
        parts.push(node.name);
        node = node.parent;
      }
      return '/' + parts.join('/');
    }

  });

  return FileManager;

});
<|endoftext|>


<commit_before>const fs = require("mz/fs");
const childProcess = require("mz/child_process");
const os = require("os");
const platform = os.platform();

const CONFIG_DIR = `${process.env.HOME}/.mehserve`;

function run(binary, args, options) {
  const cp = childProcess.spawn(binary, args, options);
  return new Promise((resolve, reject) => {
    cp.on("close", code => {
      if (code === 0) {
        resolve();
      } else {
        reject(new Error(`${binary} exited with status '${code}'`));
      }
    });
  });
}

exports.createSSLCertificateFor = async function createSSLCertificateFor(
  hostname
) {
  if (!hostname || !hostname.match(/^[a-z0-9][a-z0-9-_.]+$/)) {
    throw new Error(
      `Unsupported hostname '${hostname}' (hints: use lower case; only alphanumeric, hyphen, underscore, dot; must start alphanumeric)`
    );
  }

  const configSrc = `${CONFIG_DIR}/${hostname}.ssl.cnf`;
  const keyDest = `${CONFIG_DIR}/${hostname}.ssl.key`;
  const certDest = `${CONFIG_DIR}/${hostname}.ssl.crt`;
  await fs.writeFile(
    configSrc,
    `\
[req]
distinguished_name = req_distinguished_name
x509_extensions = v3_req
prompt = no
[req_distinguished_name]
CN = ${hostname}
[v3_req]
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = ${hostname}
DNS.2 = ${hostname}.meh
DNS.3 = ${hostname}.dev
`
  );

  await run("openssl", [
    "req",
    "-new",
    "-newkey",
    "rsa:2048",
    "-sha1",
    "-days",
    "3650",
    "-nodes",
    "-x509",
    "-keyout",
    keyDest,
    "-out",
    certDest,
    "-config",
    configSrc,
  ]);
  await fs.unlink(configSrc);
  console.log("SSL certificate successfully generated");

  if (platform === "darwin") {
    console.log(`\

================================================================================

We'll open Keychain Access in a moment. Here are the instructions what to do:

1. Click "Add"
2. Find the certificate (e.g. "${hostname}") in the "Certificates" category,
   select it, and click the [ｉ] button at the bottom
3. In the popup window, click the ▶ button to the left of 'Trust', and select
   'Always Trust' for 'When using this certificate:'.
4. Close the popup window.
5. When prompted, enter your password again and click Update Settings.
6. Quit Keychain Access.

Press enter to continue
`);

    await run("open", [
      "-a",
      "/Applications/Utilities/Keychain Access.app",
      certDest,
    ]);
  } else {
    console.log(
      `We don't have instructions for '${platform}' yet (PRs are welcome!); you need to add '${certDest}' as a trusted certificate`
    );
  }
};
<commit_msg>Use SHA256 for signing SSL certificates<commit_after>const fs = require("mz/fs");
const childProcess = require("mz/child_process");
const os = require("os");
const platform = os.platform();

const CONFIG_DIR = `${process.env.HOME}/.mehserve`;

function run(binary, args, options) {
  const cp = childProcess.spawn(binary, args, options);
  return new Promise((resolve, reject) => {
    cp.on("close", code => {
      if (code === 0) {
        resolve();
      } else {
        reject(new Error(`${binary} exited with status '${code}'`));
      }
    });
  });
}

exports.createSSLCertificateFor = async function createSSLCertificateFor(
  hostname
) {
  if (!hostname || !hostname.match(/^[a-z0-9][a-z0-9-_.]+$/)) {
    throw new Error(
      `Unsupported hostname '${hostname}' (hints: use lower case; only alphanumeric, hyphen, underscore, dot; must start alphanumeric)`
    );
  }

  const configSrc = `${CONFIG_DIR}/${hostname}.ssl.cnf`;
  const keyDest = `${CONFIG_DIR}/${hostname}.ssl.key`;
  const certDest = `${CONFIG_DIR}/${hostname}.ssl.crt`;
  await fs.writeFile(
    configSrc,
    `\
[req]
distinguished_name = req_distinguished_name
x509_extensions = v3_req
prompt = no
[req_distinguished_name]
CN = ${hostname}
[v3_req]
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = ${hostname}
DNS.2 = ${hostname}.meh
DNS.3 = ${hostname}.dev
`
  );

  await run("openssl", [
    "req",
    "-new",
    "-newkey",
    "rsa:2048",
    "-sha256",
    "-days",
    "3650",
    "-nodes",
    "-x509",
    "-keyout",
    keyDest,
    "-out",
    certDest,
    "-config",
    configSrc,
  ]);
  await fs.unlink(configSrc);
  console.log("SSL certificate successfully generated");

  if (platform === "darwin") {
    console.log(`\

================================================================================

We'll open Keychain Access in a moment. Here are the instructions what to do:

1. Click "Add"
2. Find the certificate (e.g. "${hostname}") in the "Certificates" category,
   select it, and click the [ｉ] button at the bottom
3. In the popup window, click the ▶ button to the left of 'Trust', and select
   'Always Trust' for 'When using this certificate:'.
4. Close the popup window.
5. When prompted, enter your password again and click Update Settings.
6. Quit Keychain Access.

Press enter to continue
`);

    await run("open", [
      "-a",
      "/Applications/Utilities/Keychain Access.app",
      certDest,
    ]);
  } else {
    console.log(
      `We don't have instructions for '${platform}' yet (PRs are welcome!); you need to add '${certDest}' as a trusted certificate`
    );
  }
};
<|endoftext|>


<commit_before>'use strict'
var vstamp = require('vigour-stamp')
var subscribe = require('./subscribe')

function keyToNumber (key) {
  let num = key
  // for (let i = 0, len = key.length; i < len; i++) {
  //   num += key.charCodeAt(i)
  // }
  return num
}

module.exports = {
  properties: {
    _lstamp: true,
    _sid: true,
    _subscriptions: true,
    cachedSyncPath: true
  },
  // make it easy to create different stamps when using a hub (source ids etc)
  _lstamp: 0,
  define: {
    isState: { value: true },
    keysCheck (val, key) {
      const target = val[key]
      return target &&
        target.isState &&
        !target.keyType
    },
    subscribe (subs, update, tree, stamp, attach, id) {
      return subscribe(this, subs, update, tree, stamp, attach, id)
    },
    sid () {
      if (!('_sid' in this)) {
        if (!this.key) {
          this._sid = 1
        } else {
          this._sid = this.cParent().sid() + keyToNumber(this.key)
          console.log(this._sid)
        }
      }
      return this._sid
    }
  },
  on: {
    data: {
      lstamp: function lstamp (val, stamp) {
        var parent = this.cParent()
        this._lstamp = stamp
        if (this._subscriptions) {
          let l = this
          vstamp.on(stamp, function () {
            l.emit('subscription', val, stamp)
          })
        } else {
          while (parent && parent._lstamp !== stamp) {
            lstampInner(parent, val, stamp)
            parent = parent.cParent()
          }
        }
      }
    }
  }
}

function lstampInner (parent, val, stamp) {
  parent._lstamp = stamp
  if (parent._subscriptions) {
    let l = parent
    vstamp.on(stamp, function () {
      l.emit('subscription', val, stamp)
    })
  } else if ('base' in parent.__on._data) {
    if (parent.__on._data.base) {
      parent.__on._data.base.each(function (p) {
        lstampInner(p, val, stamp)
      })
    }
  }
}
<commit_msg>hash sids<commit_after>'use strict'
var vstamp = require('vigour-stamp')
var subscribe = require('./subscribe')
var hash = require('vigour-util/hash')

// function keyToNumber (key) {
//   let num = key
//   // for (let i = 0, len = key.length; i < len; i++) {
//   //   num += key.charCodeAt(i)
//   // }
//   return num
// }

module.exports = {
  properties: {
    _lstamp: true,
    _sid: true,
    _subscriptions: true,
    cachedSyncPath: true
  },
  // make it easy to create different stamps when using a hub (source ids etc)
  _lstamp: 0,
  define: {
    isState: { value: true },
    keysCheck (val, key) {
      const target = val[key]
      return target &&
        target.isState &&
        !target.keyType
    },
    subscribe (subs, update, tree, stamp, attach, id) {
      return subscribe(this, subs, update, tree, stamp, attach, id)
    },
    sid () {
      if (!('_sid' in this)) {
        if (!this.key) {
          this._sid = 1
        } else {
          this._sid = hash(this.cParent().sid() + this.key)
          console.log(this._sid)
        }
      }
      return this._sid
    }
  },
  on: {
    data: {
      lstamp: function lstamp (val, stamp) {
        var parent = this.cParent()
        this._lstamp = stamp
        if (this._subscriptions) {
          let l = this
          vstamp.on(stamp, function () {
            l.emit('subscription', val, stamp)
          })
        } else {
          while (parent && parent._lstamp !== stamp) {
            lstampInner(parent, val, stamp)
            parent = parent.cParent()
          }
        }
      }
    }
  }
}

function lstampInner (parent, val, stamp) {
  parent._lstamp = stamp
  if (parent._subscriptions) {
    let l = parent
    vstamp.on(stamp, function () {
      l.emit('subscription', val, stamp)
    })
  } else if ('base' in parent.__on._data) {
    if (parent.__on._data.base) {
      parent.__on._data.base.each(function (p) {
        lstampInner(p, val, stamp)
      })
    }
  }
}
<|endoftext|>


<commit_before>import CssSyntaxError from './css-syntax-error';
import PreviousMap    from './previous-map';

import path from 'path';

let sequence = 0;

export default class Input {

    constructor(css, opts = { }) {
        this.css = css.toString();

        if ( this.css[0] === '\uFEFF' || this.css[0] === '\uFFFE' ) {
            this.css = this.css.slice(1);
        }

        if ( opts.from ) {
            if ( /^\w+:\/\//.test(opts.from) ) {
                this.file = opts.from;
            } else {
                this.file = path.resolve(opts.from);
            }
        }

        let map = new PreviousMap(this.css, opts);
        if ( map.text ) {
            this.map = map;
            let file = map.consumer().file;
            if ( !this.file && file ) this.file = this.mapResolve(file);
        }

        if ( !this.file ) {
            sequence += 1;
            this.id   = '<input css ' + sequence + '>';
        }
        if ( this.map ) this.map.file = this.from;
    }

    error(message, line, column, opts = { }) {
        let result;
        let origin = this.origin(line, column);
        if ( origin ) {
            result = new CssSyntaxError(message, origin.line, origin.column,
                origin.source, origin.file, opts.plugin);
        } else {
            result = new CssSyntaxError(message, line, column,
                this.css, this.file, opts.plugin);
        }

        result.input = { line, column, source: this.css };
        if ( this.file ) result.input.file = this.file;

        return result;
    }

    origin(line, column) {
        if ( !this.map ) return false;
        let consumer = this.map.consumer();

        let from = consumer.originalPositionFor({ line, column });
        if ( !from.source ) return false;

        let result = {
            file:   this.mapResolve(from.source),
            line:   from.line,
            column: from.column
        };

        let source = consumer.sourceContentFor(from.source);
        if ( source ) result.source = source;

        return result;
    }

    mapResolve(file) {
        if ( /^\w+:\/\//.test(file) ) {
            return file;
        } else {
            return path.resolve(this.map.consumer().sourceRoot || '.', file);
        }
    }

    get from() {
        return this.file || this.id;
    }

}
<commit_msg>Add JSDoc to Input<commit_after>import CssSyntaxError from './css-syntax-error';
import PreviousMap    from './previous-map';

import path from 'path';

let sequence = 0;

/**
 * @typedef  {object} Position
 * @property {string} file   - path to file
 * @property {number} line   - source line in file
 * @property {number} column - source column in file
 */

/**
 * Represents the source CSS.
 *
 * @example
 * const root  = postcss.parse(css, { from: file });
 * const input = root.source.input;
 */
class Input {

    /**
     * @param {string} css    - input CSS source
     * @param {object} [opts] - {@link Processor#process} options
     */
    constructor(css, opts = { }) {
        /**
         * @member {string} - input CSS source
         *
         * @example
         * const input = postcss.parse('a{}', { from: file }).input;
         * input.css //=> "a{}";
         */
        this.css = css.toString();

        if ( this.css[0] === '\uFEFF' || this.css[0] === '\uFFFE' ) {
            this.css = this.css.slice(1);
        }

        if ( opts.from ) {
            if ( /^\w+:\/\//.test(opts.from) ) {
                /**
                 * @member {string} - The absolute path to the CSS source file
                 *                    defined with the `from` option.
                 *
                 * @example
                 * const root = postcss.parse(css, { from: 'a.css' });
                 * root.source.input.file //=> '/home/ai/a.css'
                 */
                this.file = opts.from;
            } else {
                this.file = path.resolve(opts.from);
            }
        }

        let map = new PreviousMap(this.css, opts);
        if ( map.text ) {
            /**
             * @member {PreviousMap} - The input source map passed from
             *                         a compilation step before PostCSS
             *                         (for example, from Sass compiler).
             *
             * @example
             * root.source.input.map.consumer().sources //=> ['a.sass']
             */
            this.map = map;
            let file = map.consumer().file;
            if ( !this.file && file ) this.file = this.mapResolve(file);
        }

        if ( !this.file ) {
            sequence += 1;
            /**
             * @member {string} - The unique ID of the CSS source. It will be
             *                    created if `from` option is not provided
             *                    (because PostCSS does not know the file path).
             *
             * @example
             * const root = postcss.parse(css);
             * root.source.input.file //=> undefined
             * root.source.input.id   //=> "<input css 1>"
             */
            this.id   = '<input css ' + sequence + '>';
        }
        if ( this.map ) this.map.file = this.from;
    }

    error(message, line, column, opts = { }) {
        let result;
        let origin = this.origin(line, column);
        if ( origin ) {
            result = new CssSyntaxError(message, origin.line, origin.column,
                origin.source, origin.file, opts.plugin);
        } else {
            result = new CssSyntaxError(message, line, column,
                this.css, this.file, opts.plugin);
        }

        result.input = { line, column, source: this.css };
        if ( this.file ) result.input.file = this.file;

        return result;
    }

    /**
     * Reads the input source map and returns a symbol position
     * in the input source (e.g., in a Sass file that was compiled
     * to CSS before being passed to PostCSS).
     *
     * @param {number} line   - line in input CSS
     * @param {number} column - column in input CSS
     *
     * @return {Position} position in input source
     *
     * @example
     * root.source.input.origin(1, 1) //=> { file: 'a.css', line: 3, column: 1 }
     */
    origin(line, column) {
        if ( !this.map ) return false;
        let consumer = this.map.consumer();

        let from = consumer.originalPositionFor({ line, column });
        if ( !from.source ) return false;

        let result = {
            file:   this.mapResolve(from.source),
            line:   from.line,
            column: from.column
        };

        let source = consumer.sourceContentFor(from.source);
        if ( source ) result.source = source;

        return result;
    }

    mapResolve(file) {
        if ( /^\w+:\/\//.test(file) ) {
            return file;
        } else {
            return path.resolve(this.map.consumer().sourceRoot || '.', file);
        }
    }

    /**
     * The CSS source identifier. Contains {@link Input#file} if the user
     * set the `from` option, or {@link Input#id} if they did not.
     * @type {string}
     *
     * @example
     * var root  = postcss.parse(css, { from: 'a.css' });
     * root.source.input.from //=> "/home/ai/a.css"
     *
     * var root  = postcss.parse(css);
     * root.source.input.from //=> "<input css 1>"
     */
    get from() {
        return this.file || this.id;
    }

}

export default Input;
<|endoftext|>


<commit_before>"use strict";

const
    _tag = function (tagName, properties, children) {
        let element = document.createElementNS("http://www.w3.org/2000/svg", tagName);
        Object.keys(properties).forEach(name => {
            element.setAttribute(name, properties[name]);
        });
        [].concat(children || [])
            .map(def => typeof def === "string" ? document.createTextNode(def) : def)
            .forEach(node => element.appendChild(node));
        return element;
    },

    _svg = _tag.bind(null, "svg");

[
    "circle",
    "text",
    "path",
    "defs",
    "linearGradient",
    "stop",
    "rect"

].forEach(tag => {
    _svg[tag] = _tag.bind(null, tag);
});

module.exports = _svg;
<commit_msg>Adds group<commit_after>"use strict";

const
    _tag = function (tagName, properties, children) {
        let element = document.createElementNS("http://www.w3.org/2000/svg", tagName);
        Object.keys(properties).forEach(name => {
            element.setAttribute(name, properties[name]);
        });
        [].concat(children || [])
            .map(def => typeof def === "string" ? document.createTextNode(def) : def)
            .forEach(node => element.appendChild(node));
        return element;
    },

    _svg = _tag.bind(null, "svg");

[
    "circle",
    "text",
    "path",
    "defs",
    "linearGradient",
    "stop",
    "rect",
    "g"

].forEach(tag => {
    _svg[tag] = _tag.bind(null, tag);
});

module.exports = _svg;
<|endoftext|>


<commit_before>// Single var pattern of gulp (require) stuff in full effect!!!

var gulp = require("gulp"), // "require" gulp
    uncss = require("gulp-uncss"), // Remove unused css selectors
    minifyCSS = require("gulp-minify-css"), // Minify CSS
    csslint = require("gulp-csslint"), // Lint CSS,
    concatCss = require("gulp-concat-css"), // Concatenate CSS only
    watch = require("gulp-watch"), // Watch files changes
    imagemin = require('gulp-imagemin'), // Minifying images
    autoprefixer = require('gulp-autoprefixer'),
    exec = require('child_process').exec, // Run CLI commands via node
    Q = require('q'), // Manage promises;
    cp = require( "cp" ); // Copy files over

// End single var pattern

// Needed to run grunt tasks through gulp
require("gulp-grunt")(gulp);

/*
 *  ===================================================================
 *  | STORE PREPROCESSOR FILE REFERENCES IN VARIABLES |
 *  ===================================================================
 */
var lessFiles = ["css-build/*.less", "css-build/**/*.less"], // LESS
    coffeeFiles = ["coffee/*.coffee"], // Coffeescript

    /*
     * IGNORE ARRAY
     * ============
     * Selectors NOT to be removed when "gulp uncss" task runs.
     * Try to list them alphabetically and in the following order:
     *
     * 1. Page elements first (<nav>, <aside>, etc.)
     * 2. IDs second
     * 3. Classes third
     */
     ignoreArray = [
                     '.hide-menu',
                     '.show-menu',
                     '.showMobileMenu',
                     '.hideMobileMenu',
                     '.showSearchbox',
                     '.hideSearchbox',
                     '.hide-searchbox',
                     '.show-searchbox',
                     '.entry-header > span'
                    ];

/*
 *  ===================================================================
 *  | CSS BUILD TASK |
 *
 * Running "gulp buildcss" performs the following sequence of tasks...
 *
 * 1. Runs the "gulp less" task that"s passed as a "hint"(*)
 * 2. Concatenate selected .css files listed in "gulp.src"
 * 3. Remove unused CSS
 * 4. Minify CSS
 * 5. Lint CSS
 *
 * (*) gulp "hints" are cool...read more about them at:
 * https://github.com/gulpjs/gulp/blob/master/docs/API.md
 *  ===================================================================
 */

gulp.task('buildcss', ['less', 'concat', 'outputcss']);

// "gulp less" task
// ================
// Process .less files using node exec
// Returns a promise with q
gulp.task("less", function () {
  var deferred = Q.defer();
  setTimeout(function() {
    exec("lessc css-build/style.less > css-build/style.css");
    return deferred.promise;
  }, 1000);
});

// "gulp concat" task
// ==================
// Concatenate "css-build/styles.css" and "css-build/bootstrap.css"
// Takes "less" as a gulp hint
// Returns a promise with q
gulp.task('concat', ['less'], function() {
  var deferred = Q.defer();
  setTimeout(function() {
    return gulp.src(['css-build/wp-comment-block.css', 'css-build/font-awesome.css', 'css-build/bootstrap.css','css-build/style.css'])
    .pipe(concatCss('style.css'))
    .pipe(gulp.dest("wp-content/themes/kaidez-swiss/"));
    return deferred.promise;
  }, 1000);
});

gulp.task("outputcss", ['concat'],function () {
  var deferred = Q.defer();
  setTimeout(function() {
    gulp.src(['wp-content/themes/kaidez-swiss/style.css'])
    .pipe(autoprefixer({
      browsers: ['last 2 versions'],
      cascade: false
    }))
    .pipe(uncss({
      html: [
        'http://localhost:8888/', // home page
        'http://localhost:8888/tutorial-filter-content-with-jquery-filter-jquery-selectors/', // A single post page
        'http://localhost:8888/personal/', // A category page
        'http://localhost:8888/tutorial-filter-content-with-jquery-filter-jquery-selectors/', // A page with a lot of code formatting
        'http://localhost:8888/kdz-build-tool/',
        'http://localhost:8888/affiliate-disclaimer/', // Affiliate
        'http://localhost:8888/404.php' // 404 page
      ],
      ignore: ignoreArray
    }))
    .pipe(minifyCSS({
      keepBreaks: true
    }))
    .pipe(gulp.dest("wp-content/themes/kaidez-swiss/"))
    .pipe(csslint({
      "important": false,
      "duplicate-background-images": false,
      "ids": false,
      "text-indent": false
    }))
    return deferred.promise;
    }, 4000);
  });

// Copy q.js from "node_modules" to the kaidez-swiss theme
gulp.task("cpq", function(){
  cp.sync( "node_modules/q/q.js", "wp-content/themes/kaidez-swiss/js/libs/q.js" );
  });
/*
*  ===================================================================
*  | IMAGE MINIFICATION TASK |
*
*  Take all images in "image-min/" & minify them out to the theme
*  ===================================================================
*/
gulp.task('images', function () {
  return gulp.src('image-min/*')
  .pipe(imagemin({
    progressive: true,
    svgoPlugins: [{removeViewBox: false}]
  }))
  .pipe(gulp.dest('wp-content/themes/kaidez-swiss/img'));
});

/*
 *  ===================================================================
 *  | "gulp-grunt" TASKS...RUN GRUNT TASKS VIA GULP!!!! |
 *  ===================================================================
 */

// Run the "grunt coffee" task
gulp.task("coffee", function () {
  gulp.run("grunt-coffee");
});

// BOWERCOPY TASKS
// Copy over ALL the Bower Components!!!
gulp.task("bowercopy", function () {
  gulp.run("grunt-bowercopy");
});

// Copy over Bootstrap core .css only
gulp.task("bowerbscss", function () {
  gulp.run("grunt-bowercopy:bscss");
});

// Copy over jQuery only
gulp.task("bowerjq", function () {
  gulp.run("grunt-bowercopy:jq");
});

// Copy over prism.js only
gulp.task("prism", function () {
  gulp.run("grunt-bowercopy:prism");
});

// Copy over Font Awesome's minified CSS file
gulp.task("bowerfa", function () {
  gulp.run("grunt-bowercopy:bsfa");
});

// Copy over Font Awesome's minified CSS file
gulp.task("bsfafont", function () {
  gulp.run("grunt-bowercopy:bsfafont");
});

/*
 *  ===================================================================
 *  | START WATCH TASK |
 *
 *  Be careful of watching a lot of files because that may eat up
 *  computer memory...at least, it does in Grunt.
 *  ===================================================================
 */

gulp.task("default", function () {

  // Watch for CSS/JS file changes

  gulp.watch(lessFiles, ["buildcss"]);
  gulp.watch(coffeeFiles, ["coffee"]);
});
<commit_msg>updates to gulpfile.js (see message) -add regex checks to the uncss ignoreArray -remove coffeescript-related tasks -updated comments<commit_after>// Single var pattern of gulp (require) stuff in full effect!!!

var gulp = require("gulp"), // "require" gulp
    uncss = require("gulp-uncss"), // Remove unused css selectors
    minifyCSS = require("gulp-minify-css"), // Minify CSS
    csslint = require("gulp-csslint"), // Lint CSS,
    concatCss = require("gulp-concat-css"), // Concatenate CSS only
    watch = require("gulp-watch"), // Watch files changes
    imagemin = require('gulp-imagemin'), // Minifying images
    autoprefixer = require('gulp-autoprefixer'),
    exec = require('child_process').exec, // Run CLI commands via node
    Q = require('q'), // Manage promises;
    cp = require( "cp" ); // Copy files over

// End single var pattern

// Needed to run grunt tasks through gulp
require("gulp-grunt")(gulp);

/*
 *  ===================================================================
 *  | STORE PREPROCESSOR FILE REFERENCES IN VARIABLES |
 *  ===================================================================
 */
var lessFiles = ["css-build/*.less", "css-build/**/*.less"], // LESS
    coffeeFiles = ["coffee/*.coffee"], // Coffeescript

    /*
     * IGNORE ARRAY
     * ============
     * Selectors NOT to be removed when "gulp uncss" task runs.
     * Try to list them alphabetically and in the following order:
     *
     * 1. Page elements first (<nav>, <aside>, etc.)
     * 2. IDs second
     * 3. Classes third
     */
     ignoreArray = [
                      /aside/,
                      /aside-/,
                      /fa/,
                      /fa-/,
                      /hide-/,
                      /jump-to-top/,
                      /footer-/,
                     ".row",
                      /show-/,
                      ".col-md-4",
                     ".site-footer",
                     '.showMobileMenu',
                     '.hideMobileMenu',
                     '.showSearchbox',
                     '.hideSearchbox',
                     '.entry-header > span'
                    ];

/*
 *  ===================================================================
 *  | CSS BUILD TASK |
 *
 * Running "gulp buildcss" performs the following sequence of tasks...
 *
 * 1. Runs the "gulp less" task that"s passed as a "hint"(*)
 * 2. Concatenate selected .css files listed in "gulp.src"
 * 3. Remove unused CSS
 * 4. Minify CSS
 * 5. Lint CSS
 *
 * (*) gulp "hints" are cool...read more about them at:
 * https://github.com/gulpjs/gulp/blob/master/docs/API.md
 *  ===================================================================
 */

gulp.task('buildcss', ['less', 'concat', 'outputcss']);

// "gulp less" task
// ================
// Process .less files using node exec
// Returns a promise with q
gulp.task("less", function () {
  var deferred = Q.defer();
  setTimeout(function() {
    exec("lessc css-build/style.less > css-build/style.css");
    return deferred.promise;
  }, 1000);
});

// "gulp concat" task
// ==================
// Concatenate "css-build/styles.css" and "css-build/bootstrap.css"
// Takes "less" as a gulp hint
// Returns a promise with q
gulp.task('concat', ['less'], function() {
  var deferred = Q.defer();
  setTimeout(function() {
    return gulp.src(['css-build/wp-comment-block.css', 'css-build/font-awesome.css', 'css-build/bootstrap.css','css-build/style.css'])
    .pipe(concatCss('style.css'))
    .pipe(gulp.dest("wp-content/themes/kaidez-swiss/"));
    return deferred.promise;
  }, 1000);
});

gulp.task("outputcss", ['concat'],function () {
  var deferred = Q.defer();
  setTimeout(function() {
    gulp.src(['wp-content/themes/kaidez-swiss/style.css'])
    .pipe(autoprefixer({
      browsers: ['last 2 versions'],
      cascade: false
    }))
    .pipe(uncss({
      html: [
        'http://localhost:8888/', // home page
        'http://localhost:8888/tutorial-filter-content-with-jquery-filter-jquery-selectors/', // A single post page
        'http://localhost:8888/personal/', // A category page
        'http://localhost:8888/tutorial-filter-content-with-jquery-filter-jquery-selectors/', // A page with a lot of code formatting
        'http://localhost:8888/kdz-build-tool/',
        'http://localhost:8888/affiliate-disclaimer/', // Affiliate
        'http://localhost:8888/404.php' // 404 page
      ],
      ignore: ignoreArray
    }))
    .pipe(minifyCSS({
      keepBreaks: true
    }))
    .pipe(gulp.dest("wp-content/themes/kaidez-swiss/"))
    .pipe(csslint({
      "important": false,
      "duplicate-background-images": false,
      "ids": false,
      "text-indent": false
    }))
    return deferred.promise;
    }, 4000);
  });

// Copy q.js from "node_modules" to the kaidez-swiss theme
gulp.task("cpq", function(){
  cp.sync( "node_modules/q/q.js", "wp-content/themes/kaidez-swiss/js/libs/q.js" );
  });
/*
*  ===================================================================
*  | IMAGE MINIFICATION TASK |
*
*  Take all images in "image-min/" & minify them out to the theme
*  ===================================================================
*/
gulp.task('images', function () {
  return gulp.src('image-min/*')
  .pipe(imagemin({
    progressive: true,
    svgoPlugins: [{removeViewBox: false}]
  }))
  .pipe(gulp.dest('wp-content/themes/kaidez-swiss/img'));
});

/*
 *  ===================================================================
 *  | "gulp-grunt" TASKS...RUN GRUNT TASKS VIA GULP!!!! |
 *  ===================================================================
 */

// BOWERCOPY TASKS
// Copy over ALL the Bower Components!!!
gulp.task("bowercopy", function () {
  gulp.run("grunt-bowercopy");
});

// Copy over Bootstrap core .css only
gulp.task("bowerbscss", function () {
  gulp.run("grunt-bowercopy:bscss");
});

// Copy over jQuery only
gulp.task("bowerjq", function () {
  gulp.run("grunt-bowercopy:jq");
});

// Copy over prism.js only
gulp.task("prism", function () {
  gulp.run("grunt-bowercopy:prism");
});

// Copy over Font Awesome's minified CSS file
gulp.task("bowerfa", function () {
  gulp.run("grunt-bowercopy:bsfa");
});

// Copy over Font Awesome's minified CSS file
gulp.task("bsfafont", function () {
  gulp.run("grunt-bowercopy:bsfafont");
});

/*
 *  ===================================================================
 *  | START WATCH TASK |
 *
 *  Be careful of watching a lot of files because that may eat up
 *  computer memory...at least, it does in Grunt.
 *  ===================================================================
 */

gulp.task("default", function () {

  // Watch for CSS file changes

  gulp.watch(lessFiles, ["buildcss"]);

});
<|endoftext|>


<commit_before>'use strict';

var _ = require('underscore'),
  Irc = require('irc'),
  Slacker = require('./slacker'),
  IrcMsgEncoder = require('./irc-msg-encoder');

var IrcBot = function(config) {
  if (!(this instanceof IrcBot)) {
    return new IrcBot(config);
  }

  this.config = _.defaults(config, {
    server: 'irc.freenode.com', // IRC server
    username: 'slackbot',       // Bot IRC nickname
    nick: 'slackbot',

    channels: {}, // Map of ircChannels : slackChannels
    users: {},    // Map of ircLogin : slackUsernames

    isSilent: false,
    isSystemSilent: true,
    isUsersTracking: true,
    isAutoTildeAdded: false,
    isMapName: true,
    isMapAvatar: true
  });

  this._configureClient();
  this.client = new Irc.Client(this.config.server, this.config.nick, this.irc);
  this.slacker = new Slacker(config);

  if (config.isUsersTracking) {
    this._trackUsers();
  }

  this._handleErrors();
  return this;
};

IrcBot.prototype._configureClient = function() {
  var nodeIrcOptions = [
    'floodProtection', 'port', 'debug', 'showErrors', 'autoRejoin',
    'autoConnect', 'secure', 'selfSigned', 'certExpired',
    'floodProtection', 'floodProtectionDelay', 'sasl', 'stripColors',
    'channelPrefixes', 'messageSplit'
  ];

  this.irc = {
    userName: this.config.username,
    channels: Object.keys(this.config.channels),
    floodProtection: true
  };
  this.config.ircChannels = _.values(this.config.channels);

  this._usermap = {
    users: this.config.users || {},
    nicks: {}
  };

  nodeIrcOptions.forEach(function(opt) {
    if (this.config[opt]) {
      this.irc[opt] = this.config[opt];
    }
  }.bind(this));

  if (this.config.isAutoTildeAdded) {
    this._addTiledePrefix();
  }
};

IrcBot.prototype._addTiledePrefix = function() {
  Object.keys(this.config.users).forEach(function(username) {
    if (username[0] !== '~') {
      this.config.users['~' + username] = this.config.users[username];
      delete this.config.users[username];
    }
  }.bind(this));
};

IrcBot.prototype._handleErrors = function() {
  this.client.addListener('error', function(message) {
    var
    channel = message.args[1],
    errorMessage = this.mapPronouns(message.args[2]);

    this.systemSpeak(channel, 'I don\'t feel so well because ' + errorMessage);
  }.bind(this));
};

IrcBot.prototype._trackUsers = function() {
  var myusername = '~' + this.config.username;

  // On entrance, track all existing names
  this.client.addListener('names', function(channel, nicks) {
    Object.keys(nicks).forEach(function(nick) {
      this.client.whois(nick, function(whois) {
        if (whois.user === myusername) {
          return;
        }
        this._usermap.nicks[nick] = this._usermap.users[whois.user];
      }.bind(this));
    }.bind(this));
    this.systemSpeak(channel, 'I\'m all over you slackers');
  }.bind(this));

  // New user has joined, match him up
  this.client.addListener('join', function(channel, nick, whois) {
    if (whois.user === myusername) {
      return;
    }
    else {
      this._usermap.nicks[nick] = this._usermap.users[whois.user];
      this.systemSpeak(channel, 'i\'m watching you slacker @' +
             this._usermap.nicks[nick]);
    }
  }.bind(this));

  // Existing user has changed nickname
  this.client.addListener('nick', function(oldNick, newNick, channels) {
    if (newNick === this.config.nick) {
      return;
    }
    this._usermap.nicks[newNick] = this._usermap.nicks[oldNick];
    delete this._usermap.nicks[oldNick];
    channels.forEach(function(channel) {
      this.systemSpeak(channel, 'don\'t think you can hide slacker @' +
             this._usermap.nicks[newNick]);
    }.bind(this));
  }.bind(this));
};

IrcBot.prototype.listen = function() {
  this.client.addListener('message', this.sentToSlack.bind(this));
};

IrcBot.prototype.sentToSlack = function(from, to, message) {
  var
  username = from,
  avatar = this.config.iconUrl,
  avatars = this.config.avatarMap,
  hasAvatar = false;

  if (this.config.isMapName && !!this._usermap.nicks[from]) {
    username = this._usermap.nicks[from];
    hasAvatar = typeof avatars[username] !== 'undefined';
  }

  if (this.config.isMapAvatar && hasAvatar) {
    avatar = avatars[username];
  }

  if (!message.match(/.+url.*:\ \[.+\]/)) {
    this.slacker.send('chat.postMessage', {
      channel: this.config.ircChannels[0],
      text: this.prepareMessage(message),
      username: username,
      icon_url: avatar,
      icon_emoji: this.config.iconEmoji,
      unfurl_links: true
    });
  }
};

IrcBot.prototype.speak = function(channel, message) {
  if (!this.config.isSilent) {
    this.client.say(channel, message);
  }
};

IrcBot.prototype.systemSpeak = function(channel, message) {
  if (!this.config.isSystemSilent) {
    this.speak(channel, message);
  }
};

IrcBot.prototype.prepareMessage = function(message) {
  return IrcMsgEncoder(message)
    .setChanelMap(this.config.channels)
    .setUserMap(this.config.users)
    .setNickMap(this._usermap.nicks)
    .autoMention().encodeMention().toString();
};

IrcBot.prototype.mapPronouns = function(message) {
  var map = {
    'you': 'i',
    'you\'re': 'i\'m'
  };
  return message.split(' ').map(function(word) {
    return map[word.toLowerCase()] ? map[word.toLowerCase()] : word;
  }).join(' ');
};

module.exports = IrcBot;
<commit_msg>Refactor track method of IRC Bot<commit_after>'use strict';

var _ = require('underscore'),
  Irc = require('irc'),
  Slacker = require('./slacker'),
  IrcMsgEncoder = require('./irc-msg-encoder');

var IrcBot = function(config) {
  if (!(this instanceof IrcBot)) {
    return new IrcBot(config);
  }

  this.config = _.defaults(config, {
    server: 'irc.freenode.com', // IRC server
    username: 'slackbot',       // Bot IRC nickname
    nick: 'slackbot',

    channels: {}, // Map of ircChannels : slackChannels
    users: {},    // Map of ircLogin : slackUsernames

    isSilent: false,
    isSystemSilent: true,
    isUsersTracking: true,
    isAutoTildeAdded: false,
    isMapName: true,
    isMapAvatar: true
  });

  this._configureClient();
  this.client = new Irc.Client(this.config.server, this.config.nick, this.irc);
  this.slacker = new Slacker(config);

  if (config.isUsersTracking) {
    this._trackUsers();
  }

  this._handleErrors();
  return this;
};

IrcBot.prototype._configureClient = function() {
  var nodeIrcOptions = [
    'floodProtection', 'port', 'debug', 'showErrors', 'autoRejoin',
    'autoConnect', 'secure', 'selfSigned', 'certExpired',
    'floodProtection', 'floodProtectionDelay', 'sasl', 'stripColors',
    'channelPrefixes', 'messageSplit'
  ];

  this.irc = {
    userName: this.config.username,
    channels: Object.keys(this.config.channels),
    floodProtection: true
  };
  this.config.ircChannels = _.values(this.config.channels);

  this._usermap = {
    users: this.config.users || {},
    nicks: {}
  };

  nodeIrcOptions.forEach(function(opt) {
    if (this.config[opt]) {
      this.irc[opt] = this.config[opt];
    }
  }.bind(this));

  if (this.config.isAutoTildeAdded) {
    this._addTiledePrefix();
  }
};

IrcBot.prototype._addTiledePrefix = function() {
  Object.keys(this.config.users).forEach(function(username) {
    if (username[0] !== '~') {
      this.config.users['~' + username] = this.config.users[username];
      delete this.config.users[username];
    }
  }.bind(this));
};

IrcBot.prototype._handleErrors = function() {
  this.client.addListener('error', function(message) {
    var
    channel = message.args[1],
    errorMessage = this.mapPronouns(message.args[2]);

    this.systemSpeak(channel, 'I don\'t feel so well because ' + errorMessage);
  }.bind(this));
};

IrcBot.prototype._trackUsers = function() {
  this._trackUserExisting();
  this._trackUserEntering();
  this._trackUserNicking();
};

IrcBot.prototype._trackUserExisting = function() {
  this.client.addListener('names', function(channel, nicks) {
    Object.keys(nicks).forEach(function(nick) {
      var botUsername = '~' + this.config.username;
      this.client.whois(nick, function(whois) {
        if (whois.user === botUsername) {
          return;
        }
        this._usermap.nicks[nick] = this._usermap.users[whois.user];
      }.bind(this));
    }.bind(this));
    this.systemSpeak(channel, 'I\'m all over you slackers');
  }.bind(this));
};

IrcBot.prototype._trackUserEntering = function() {
  this.client.addListener('join', function(channel, nick, whois) {
    var botUsername = '~' + this.config.username;
    if (whois.user === botUsername) {
      return;
    }
    else {
      this._usermap.nicks[nick] = this._usermap.users[whois.user];
      this.systemSpeak(channel, 'i\'m watching you slacker @' +
             this._usermap.nicks[nick]);
    }
  }.bind(this));
};

IrcBot.prototype._trackUserNicking = function() {
  this.client.addListener('nick', function(oldNick, newNick, channels) {
    if (newNick === this.config.nick) {
      return;
    }
    this._usermap.nicks[newNick] = this._usermap.nicks[oldNick];
    delete this._usermap.nicks[oldNick];
    channels.forEach(function(channel) {
      this.systemSpeak(channel, 'don\'t think you can hide slacker @' +
             this._usermap.nicks[newNick]);
    }.bind(this));
  }.bind(this));
};

IrcBot.prototype.listen = function() {
  this.client.addListener('message', this.sentToSlack.bind(this));
};

IrcBot.prototype.sentToSlack = function(from, to, message) {
  var
  username = from,
  avatar = this.config.iconUrl,
  avatars = this.config.avatarMap,
  hasAvatar = false;

  if (this.config.isMapName && !!this._usermap.nicks[from]) {
    username = this._usermap.nicks[from];
    hasAvatar = typeof avatars[username] !== 'undefined';
  }

  if (this.config.isMapAvatar && hasAvatar) {
    avatar = avatars[username];
  }

  if (!message.match(/.+url.*:\ \[.+\]/)) {
    this.slacker.send('chat.postMessage', {
      channel: this.config.ircChannels[0],
      text: this.prepareMessage(message),
      username: username,
      icon_url: avatar,
      icon_emoji: this.config.iconEmoji,
      unfurl_links: true
    });
  }
};

IrcBot.prototype.speak = function(channel, message) {
  if (!this.config.isSilent) {
    this.client.say(channel, message);
  }
};

IrcBot.prototype.systemSpeak = function(channel, message) {
  if (!this.config.isSystemSilent) {
    this.speak(channel, message);
  }
};

IrcBot.prototype.prepareMessage = function(message) {
  return IrcMsgEncoder(message)
    .setChanelMap(this.config.channels)
    .setUserMap(this.config.users)
    .setNickMap(this._usermap.nicks)
    .autoMention().encodeMention().toString();
};

IrcBot.prototype.mapPronouns = function(message) {
  var map = {
    'you': 'i',
    'you\'re': 'i\'m'
  };
  return message.split(' ').map(function(word) {
    return map[word.toLowerCase()] ? map[word.toLowerCase()] : word;
  }).join(' ');
};

module.exports = IrcBot;
<|endoftext|>