<commit_before>/*
   Copyright 2011 Future Platforms

   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.
*/

var Native = require("./Native"),
    slice = Array.prototype.slice,
    native2js = {},
    callbacks = {},
    loadedObjects = {},
    // XXX 'screens' is set by GWT-Exporter.
    gwtClasses = Native.getGlobal().screens || {},
    _ = require("underscore"),
    myRequire = require;

/**********************************************************************/
/* Utility methods to help calling native from Javascript and vice versa.
 **********************************************************************/

/*
 * Make a wrap function which takes a "NativeObject" and some method names, and turns it into
 * something we can call with a more JS-like syntax:
 * e.g.
 * Native.exec("NativeObject.make_for_", "something", "me");
 * NativeObject.make_for_("something", "me");
 * should be functionally equivalent.
 */

var tokenGenerator = (function() {
    var counter = 0;
    return function (jsName, methodName) {
        var prefix = (arguments.length >= 2) ?
                jsName + methodName : "anon.";
        if (typeof prefix !== 'string'){
            prefix = "noname.";
        }
        return prefix + (counter ++);
    };
}());

var wrapCallback = function (callback, jsName, methodName) {
    if (typeof callback !== 'function') {
        return null;
    }
    var token = tokenGenerator(jsName, methodName);
    callbacks[token] = callback;
    return token;
};

var wrapCallbacks = function (config, moduleName) {
    if (typeof config !== 'object') {
        throw new Error("First argument of wrapCallbacks should be an object");
    }
    if (typeof moduleName !== 'string') {
        moduleName = "anonymousModule";
    }
    moduleName += ".";
    var key, value;
    for (key in config) {
        if (config.hasOwnProperty(key)) {
            value = config[key];
            if (typeof value === 'function') {
                config[key] = wrapCallback(_.bind(value, config), moduleName, key);
            }
        }
    }
    return config;
};

var createWrappingCall = function (jsName, methodName) {
    return function () {
        var args = slice.call(arguments, 0);
        var i = 0, max;
        for (max=args.length; i<max; i++) {
            if (typeof args[i] === 'function') {
                args[i] = wrapCallback(args[i], jsName, methodName);
            }
        }
        args.unshift(jsName + "." + methodName);
        return Native.exec.apply(null, args);
    };
};

function createProxy (className, methodNames) {
    var proxy, i=0, max;
    if (!_.isArray(methodNames)) {
        proxy = methodNames; // methodNames is the object.
    } else {
        proxy = {};
        for (max=methodNames.length; i<max; i++) {
            var methodName = methodNames[i];
            proxy[methodName] = createWrappingCall(className, methodName);
        }
    }
    return proxy;
}

function handleError(during, e) {

    /*
     *    {
            "message": "Can't find variable: cardObject",
            "line": 505,
            "sourceId": 250182872,
            "sourceURL": "file:///Users/james/Library/Application%20Support/iPhone%20Simulator/4.3.2/Applications/ADE774FF-B033-46A2-9CBA-DD362196E1F7/Moo.app/generated-javascript//src/controller/CardController.js",
            "expressionBeginOffset": 22187,
            "expressionCaretOffset": 22197,
            "expressionEndOffset": 22197
        }
     */
    console.error("-------------------------------------------------");
    console.error("Exception found " + during);
    console.error("Message: " + e.message);
    console.error("at     : " + e.line);
    var filename = e.sourceURL || "unknown";
    /*jshint regexp:false*/
    filename = filename.replace(/.*generated-javascript\//, "");
    /*jshint regexp:true*/
    console.error("file   : " + filename);
    console.error("url    : " + e.sourceURL);

    var stack = e.stack || e.stacktrace;

    if (stack) {
        console.error(stack);
    } else {
        console.log("No stack trace");
    }
}

function resolveModule(moduleName) {
    if (loadedObjects[moduleName]) {
        return loadedObjects[moduleName];
    }
    var aModule, anObject, MyModule;
    if (gwtClasses) {
        aModule = gwtClasses[moduleName];
    }
    if (!aModule) {
        try {
            aModule = myRequire(moduleName);
        } catch (e) {
            // we don't need to do anymore,
            // as require will have reported the problem
        }
    }

    if (aModule) {
        if (typeof aModule === 'function') {
            MyModule = aModule;
            anObject = new MyModule();
        } else {
            anObject = aModule;
        }
        loadedObjects[moduleName] = anObject;
        return anObject;
    }

    // we've tried require, and gwt modules.
    // require() will have reported the error already.
    //throw new Error("Cannot load module " + moduleName);
}
exports.resolveModule = native2js.resolveModule = resolveModule;

native2js.loadProxyForModule = function (moduleName, methodNames) {
    var proxy = createProxy(moduleName, methodNames);
    var module_;
    try {
        module_ = resolveModule(moduleName);
    } catch (e) {
        handleError("resolving module " + moduleName, e);
        return;
    }
    if (module_) {
        try {
            module_.onLoad(proxy);
        } catch (e1) {
            handleError("loading module " + moduleName, e1);
        }
    }

};

native2js.unloadProxyForModule = function (moduleName) {
    try {
        var module_ = resolveModule(moduleName);
        // TODO unrequire
        // should we unrequire by removing it from the loadedObjects list.
        // this will have most effect on things setting module.exports = MyClass, e.g. GWT
        module_.onUnload();
    } catch (e) {
        handleError("unloading module", e);
    }
};

native2js.execMethod = function (moduleName, methodName, argsList) {
    var module = resolveModule(moduleName);

    if (!module) {
        console.error("No such module " + moduleName);
        return;
    }

    if (typeof module[methodName] !== 'function') {
        console.warn("Module " + moduleName + " does not have a method " + methodName);
        return;
    }

    try {
        if (argsList) {
            module[methodName].apply(module, argsList);
        } else {
            module[methodName]();
        }
    } catch (e) {
        handleError("executing " + moduleName + "." + methodName, e);
    }
};

native2js.execCallback = function (callbackId, argsList) {
    var callback = callbacks[callbackId];
    if (!callback) {
        console.warn("Callback " + callbackId + " doesn't exist");
        return;
    }
    if (typeof callback !== 'function') {
        console.error("Problem calling callback '" + callbackId + "'");
        return;
    }

    try {
        if (argsList) {
            callback.apply(null, argsList);
        } else {
            callback();
        }
    } catch (e) {
        handleError("calling callback " + callbackId + JSON.stringify(argsList), e);
    }
};

/* Once a callback has been finished with, then it should
 * be removed from the Javascript context.
 *
 * It will be up to the caller to delete the callbacks when
 * they've finished with them
 * (difference b/w callback and listener).
 */
native2js.deleteCallback = function (args) {
    var i=0, max;
    for (max=args.length; i<max; i++) {
        delete callbacks[args[i]];
    }
};

/**
 * Let native see all these functions.
 *
 */
Native.exposeToNative("native2js", native2js);

/**
 * As a convenience, let javascript see them too.
 * It may turn out we don't need any of these functions.
 */
exports.native2js = native2js;

/**********************************************************************/
/* Expose the proxies to Javascript.
 **********************************************************************/

exports.wrapCallback = wrapCallback;
exports.wrapCallbacks = wrapCallbacks;
    exports.exposeToNative = Native.exposeToNative;
<commit_msg>Documentation.<commit_after>/*
   Copyright 2011 Future Platforms

   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.
*/

var Native = require("./Native"),
    slice = Array.prototype.slice,
    native2js = {},
    callbacks = {},
    loadedObjects = {},
    // XXX 'screens' is set by GWT-Exporter.
    gwtClasses = Native.getGlobal().screens || {},
    _ = require("underscore"),
    myRequire = require;

/**********************************************************************/
/* Utility methods to help calling native from Javascript and vice versa.
 **********************************************************************/

/*
 * Make a wrap function which takes a "NativeObject" and some method names, and turns it into
 * something we can call with a more JS-like syntax:
 * e.g.
 * Native.exec("NativeObject.make_for_", "something", "me");
 * NativeObject.make_for_("something", "me");
 * should be functionally equivalent.
 */

var tokenGenerator = (function() {
    var counter = 0;
    return function (jsName, methodName) {
        var prefix = (arguments.length >= 2) ?
                jsName + methodName : "anon.";
        if (typeof prefix !== 'string'){
            prefix = "noname.";
        }
        return prefix + (counter ++);
    };
}());

var wrapCallback = function (callback, jsName, methodName) {
    if (typeof callback !== 'function') {
        return null;
    }
    var token = tokenGenerator(jsName, methodName);
    callbacks[token] = callback;
    return token;
};

var wrapCallbacks = function (config, moduleName) {
    if (typeof config !== 'object') {
        throw new Error("First argument of wrapCallbacks should be an object");
    }
    if (typeof moduleName !== 'string') {
        moduleName = "anonymousModule";
    }
    moduleName += ".";
    var key, value;
    for (key in config) {
        if (config.hasOwnProperty(key)) {
            value = config[key];
            if (typeof value === 'function') {
                config[key] = wrapCallback(_.bind(value, config), moduleName, key);
            }
        }
    }
    return config;
};

var createWrappingCall = function (jsName, methodName) {
    return function () {
        var args = slice.call(arguments, 0);
        var i = 0, max;
        for (max=args.length; i<max; i++) {
            if (typeof args[i] === 'function') {
                args[i] = wrapCallback(args[i], jsName, methodName);
            }
        }
        args.unshift(jsName + "." + methodName);
        return Native.exec.apply(null, args);
    };
};

function createProxy (className, methodNames) {
    var proxy, i=0, max;
    if (!_.isArray(methodNames)) {
        proxy = methodNames; // methodNames is the object.
    } else {
        proxy = {};
        for (max=methodNames.length; i<max; i++) {
            var methodName = methodNames[i];
            proxy[methodName] = createWrappingCall(className, methodName);
        }
    }
    return proxy;
}

/**
 * This is a generic error handler.
 *
 * It does its best to report a stack trace, or an error or *something*.
 * Unfortunately, some devices are better suported than others.
 * e.g. Android >2.0, iOS 4.3 and iOS 6.0 are pretty good at providing errors (e.g. file location, stack trace etc)
 * iOS5 was shockingly bad.
 *
 * @param during - what were you doing when you saw this error. This is a string.
 * @param e - the error itself. This, hopefully, is an Error object.
 */
function handleError(during, e) {
    /*
     *    {
            "message": "Can't find variable: cardObject",
            "line": 505,
            "sourceId": 250182872,
            "sourceURL": "file:///Users/james/Library/Application%20Support/iPhone%20Simulator/4.3.2/Applications/ADE774FF-B033-46A2-9CBA-DD362196E1F7/Moo.app/generated-javascript//src/controller/CardController.js",
            "expressionBeginOffset": 22187,
            "expressionCaretOffset": 22197,
            "expressionEndOffset": 22197
        }
     */
    console.error("-------------------------------------------------");
    console.error("Exception found " + during);
    console.error("Message: " + e.message);
    console.error("at     : " + e.line);
    var filename = e.sourceURL || "unknown";
    /*jshint regexp:false*/
    filename = filename.replace(/.*generated-javascript\//, "");
    /*jshint regexp:true*/
    console.error("file   : " + filename);
    console.error("url    : " + e.sourceURL);

    var stack = e.stack || e.stacktrace;

    if (stack) {
        console.error(stack);
    } else {
        console.log("No stack trace");
    }
}

/**
 * Finds the named module.
 *
 * It tries quite hard to do this, looking at where gwtClasses may be stashed, and the browserify.require mechanism.
 *
 * If the resulting object is a function, then it is assumed that the function is a constructor, and an object is newed.
 *
 * Once we have an object to return, we keep it around as a cache. This is to preserve the require-like semantics,
 * and also to allow multiple calls to the same object from native.
 *
 * @param moduleName
 * @returns anObject.
 */
function resolveModule(moduleName) {
    if (loadedObjects[moduleName]) {
        return loadedObjects[moduleName];
    }
    var aModule, anObject, MyModule;
    if (gwtClasses) {
        aModule = gwtClasses[moduleName];
    }
    if (!aModule) {
        try {
            aModule = myRequire(moduleName);
        } catch (e) {
            // we don't need to do anymore,
            // as require will have reported the problem
        }
    }

    if (aModule) {
        if (typeof aModule === 'function') {
            MyModule = aModule;
            anObject = new MyModule();
        } else {
            anObject = aModule;
        }
        loadedObjects[moduleName] = anObject;
        return anObject;
    }

    // we've tried require, and gwt modules.
    // require() will have reported the error already.
    //throw new Error("Cannot load module " + moduleName);
}
exports.resolveModule = native2js.resolveModule = resolveModule;

/**
 * This is the eventual recipient of the native kirinHelper.onLoad method, which is called from the bindScreen() methods.
 *
 * This receives a module name, and a list of methods that a native method implements.
 *
 * From the list of methods, a proxy object is constructed, complete with matching methods. On calling any of these
 * methods, kirin will try hard to call the corresponding methods in native.
 *
 * A module is found with the passed name, and if an onLoad method exists, then the proxy is passed to it.
 */
native2js.loadProxyForModule = function (moduleName, methodNames) {
    var proxy = createProxy(moduleName, methodNames);
    var module_;
    try {
        module_ = resolveModule(moduleName);
    } catch (e) {
        handleError("resolving module " + moduleName, e);
        return;
    }
    if (module_) {
        try {
            module_.onLoad(proxy);
        } catch (e1) {
            handleError("loading module " + moduleName, e1);
        }
    }

};

/**
 * A corresponding method to loadProxyForModule()
 */
native2js.unloadProxyForModule = function (moduleName) {
    try {
        var module_ = resolveModule(moduleName);
        // TODO unrequire
        // should we unrequire by removing it from the loadedObjects list.
        // this will have most effect on things setting module.exports = MyClass, e.g. GWT
        module_.onUnload();
    } catch (e) {
        handleError("unloading module", e);
    }
};

/**
 * Find the named module, check if there is a method with the correct name, then call it.
 */
native2js.execMethod = function (moduleName, methodName, argsList) {
    var module = resolveModule(moduleName);

    if (!module) {
        console.error("No such module " + moduleName);
        return;
    }

    if (typeof module[methodName] !== 'function') {
        console.warn("Module " + moduleName + " does not have a method " + methodName);
        return;
    }

    try {
        if (argsList) {
            module[methodName].apply(module, argsList);
        } else {
            module[methodName]();
        }
    } catch (e) {
        handleError("executing " + moduleName + "." + methodName, e);
    }
};

native2js.execCallback = function (callbackId, argsList) {
    var callback = callbacks[callbackId];
    if (!callback) {
        console.warn("Callback " + callbackId + " doesn't exist");
        return;
    }
    if (typeof callback !== 'function') {
        console.error("Problem calling callback '" + callbackId + "'");
        return;
    }

    try {
        if (argsList) {
            callback.apply(null, argsList);
        } else {
            callback();
        }
    } catch (e) {
        handleError("calling callback " + callbackId + JSON.stringify(argsList), e);
    }
};

/* Once a callback has been finished with, then it should
 * be removed from the Javascript context.
 *
 * It will be up to the caller to delete the callbacks when
 * they've finished with them
 * (difference b/w callback and listener).
 */
native2js.deleteCallback = function (args) {
    var i=0, max;
    for (max=args.length; i<max; i++) {
        delete callbacks[args[i]];
    }
};

/**
 * Let native see all these functions.
 *
 */
Native.exposeToNative("native2js", native2js);

/**
 * As a convenience, let javascript see them too.
 * It may turn out we don't need any of these functions.
 */
exports.native2js = native2js;

/**********************************************************************/
/* Expose the proxies to Javascript.
 **********************************************************************/

exports.wrapCallback = wrapCallback;
exports.wrapCallbacks = wrapCallbacks;
    exports.exposeToNative = Native.exposeToNative;
<|endoftext|>


<commit_before>// Load in the dependencies
var fs = require('fs');
var async = require('async');
var Backbone = require('backbone');
// TODO: Load these on initialize?
var competitionsJson = require('./competitions');
var CompetitionProject = require('./competition-project');
var contributionsJson = require('./contributions');
var scriptsJson = require('./scripts');
var ScriptProject = require('./script-project');

// Define common collections
var ProjectCollection = Backbone.Collection.extend({
  initialize: function (models, options) {
    Backbone.Collection.prototype.initialize.call(this, models);
    this.filepath = options.filepath;
  },
  save: function (cb) {
    fs.writeFile(this.filepath, this.toJSON(), cb);
  }
});
var CompetitionCollection = ProjectCollection.extend({
  model: CompetitionProject
});
var ScriptCollection = ProjectCollection.extend({
  model: ScriptProject
});

// Create models for each type
var competitionModels = competitionsJson.map(function (competitionJson) {
  return new CompetitionProject(competitionJson);
});
var contributionModels = contributionsJson.map(function (contributionJson) {
  return new ScriptProject(contributionJson);
});
var scriptModels = scriptsJson.map(function (scriptJson) {
  return new ScriptProject(scriptJson);
});

// Generate and return collections for each project type
exports.competitions = new CompetitionCollection(competitionModels, {
  filepath: __dirname + '/competitions.json'
});
exports.contributions = new ScriptCollection(contributionModels, {
  filepath: __dirname + '/contributions.json'
});
exports.scripts = new ScriptCollection(scriptModels, {
  filepath: __dirname + '/scripts.json'
});

exports.update  = function (cb) {
  // TODO: Invoke async.parallel + update on all projects
  process.nextTick(cb);
};
exports.save = function (done) {
  // TODO: Save each toJSON format to disk
  async.parallel([
    function saveCompetitions (cb) {
      exports.competitions.save(cb);
    },
    function saveContributions (cb) {
      exports.contributions.save(cb);
    },
    function saveScripts (cb) {
      exports.scripts.save(cb);
    }
  ], done);
};

exports.toJSON = function () {
  return {
    competitions: this.competitions.toJSON(),
    contributions: this.contributions.toJSON(),
    scripts: this.scripts.toJSON()
  };
};
<commit_msg>Fixed up JSON.stringify<commit_after>// Load in the dependencies
var fs = require('fs');
var async = require('async');
var Backbone = require('backbone');
// TODO: Load these on initialize?
var competitionsJson = require('./competitions');
var CompetitionProject = require('./competition-project');
var contributionsJson = require('./contributions');
var scriptsJson = require('./scripts');
var ScriptProject = require('./script-project');

// Define common collections
var ProjectCollection = Backbone.Collection.extend({
  initialize: function (models, options) {
    Backbone.Collection.prototype.initialize.call(this, models);
    this.filepath = options.filepath;
  },
  save: function (cb) {
    fs.writeFile(this.filepath, JSON.stringify(this.toJSON(), null, 2), cb);
  }
});
var CompetitionCollection = ProjectCollection.extend({
  model: CompetitionProject
});
var ScriptCollection = ProjectCollection.extend({
  model: ScriptProject
});

// Create models for each type
var competitionModels = competitionsJson.map(function (competitionJson) {
  return new CompetitionProject(competitionJson);
});
var contributionModels = contributionsJson.map(function (contributionJson) {
  return new ScriptProject(contributionJson);
});
var scriptModels = scriptsJson.map(function (scriptJson) {
  return new ScriptProject(scriptJson);
});

// Generate and return collections for each project type
exports.competitions = new CompetitionCollection(competitionModels, {
  filepath: __dirname + '/competitions.json'
});
exports.contributions = new ScriptCollection(contributionModels, {
  filepath: __dirname + '/contributions.json'
});
exports.scripts = new ScriptCollection(scriptModels, {
  filepath: __dirname + '/scripts.json'
});

exports.update  = function (cb) {
  // TODO: Invoke async.parallel + update on all projects
  process.nextTick(cb);
};
exports.save = function (done) {
  // TODO: Save each toJSON format to disk
  async.parallel([
    function saveCompetitions (cb) {
      exports.competitions.save(cb);
    },
    function saveContributions (cb) {
      exports.contributions.save(cb);
    },
    function saveScripts (cb) {
      exports.scripts.save(cb);
    }
  ], done);
};

exports.toJSON = function () {
  return {
    competitions: this.competitions.toJSON(),
    contributions: this.contributions.toJSON(),
    scripts: this.scripts.toJSON()
  };
};
<|endoftext|>


<commit_before>const version = "0.1.0";
const cacheName = 'lightsout-'+version;

function clearCache(){
    caches.keys().then(function(names) {
    for (let name of names){
        if (name!=cacheName){
        caches.delete(name);
            }
    }

});
}

self.addEventListener('install', e => {
  e.waitUntil(
    caches.open(cacheName).then(cache => {
      return cache.addAll([
        '/',
        '/index.html',
        '/donate.html',
        '/help.html',
        '/404.html',
        '/css/awsome/less/animated.less',
        '/css/awsome/less/bordered-pulled.less',
        '/css/awsome/less/core.less',
        '/css/awsome/less/fixed-width.less',
        '/css/awsome/less/font-awesome.less',
        '/css/awsome/less/icons.less',
        '/css/awsome/less/larger.less',
        '/css/awsome/less/list.less',
        '/css/awsome/less/mixins.less',
        '/css/awsome/less/path.less',
        '/css/awsome/less/rotated-flipped.less',
        '/css/awsome/less/screen-reader.less',
        '/css/awsome/less/stacked.less',
        '/css/awsome/less/variables.less',
        '/css/awsome/scss/font-awesome.scss',
        '/css/awsome/css/font-awesome.min.css',
        '/css/style.css',
        '/css/themes/google/google.css',
        '/css/sweetalert.css',
        '/images/favicon/6.png',
        '/images/favicon/5.png',
        '/images/favicon/4.png',
        '/images/favicon/3.png',
        '/images/favicon/2.png',
        '/images/favicon/1.png',
        '/images/help.gif',
        '/images/donate.jpg',
        '/images/star.png',
        '/images/sun.png',
        '/images/hurry.png',
        '/images/timeup.png',
        '/images/og-icon.png',
        '/manifest.json',
        '/fonts/JuliusSansOne-Regular.ttf',
        '/fonts/Sacramento-Regular.ttf',
        '/fonts/Inconsolata.otf',
        '/css/awsome/fonts/FontAwesome.otf',
        '/css/awsome/fonts/fontawesome-webfont.eot',
        '/css/awsome/fonts/fontawesome-webfont.svg',
        '/css/awsome/fonts/fontawesome-webfont.ttf',
        '/css/awsome/fonts/fontawesome-webfont.woff',
        '/css/awsome/fonts/fontawesome-webfont.woff2',
        '/js/sweetalert.min.js',
        '/js/shortcut.js',
        '/js/script.js',
        '/js/particles.min.js',
        '/js/app.js',
        '/favicon.ico'

      ])
          .then(() => self.skipWaiting());
    })
  );
});

self.addEventListener('activate', event => {
  clearCache();
  event.waitUntil(self.clients.claim());
});

self.addEventListener('fetch', event => {
  event.respondWith(
    caches.open(cacheName)
      .then(cache => cache.match(event.request, {ignoreSearch: true}))
      .then(response => {
      return response || fetch(event.request);
    })
  );
});<commit_msg>fix : service-worker.js updated #29<commit_after>const version = "0.1.1";
const cacheName = 'lightsout-'+version;

function clearCache(){
    caches.keys().then(function(names) {
    for (let name of names){
        if (name!=cacheName){
        caches.delete(name);
            }
    }

});
}

self.addEventListener('install', e => {
  e.waitUntil(
    caches.open(cacheName).then(cache => {
      return cache.addAll([
        '/',
        '/index.html',
        '/donate.html',
        '/help.html',
        '/404.html',
        '/css/awsome/less/animated.less',
        '/css/awsome/less/bordered-pulled.less',
        '/css/awsome/less/core.less',
        '/css/awsome/less/fixed-width.less',
        '/css/awsome/less/font-awesome.less',
        '/css/awsome/less/icons.less',
        '/css/awsome/less/larger.less',
        '/css/awsome/less/list.less',
        '/css/awsome/less/mixins.less',
        '/css/awsome/less/path.less',
        '/css/awsome/less/rotated-flipped.less',
        '/css/awsome/less/screen-reader.less',
        '/css/awsome/less/stacked.less',
        '/css/awsome/less/variables.less',
        '/css/awsome/scss/font-awesome.scss',
        '/css/awsome/css/font-awesome.min.css',
        '/css/style.css',
        '/css/themes/google/google.css',
        '/css/sweetalert.css',
        '/images/favicon/9.png',
        '/images/favicon/8.png',
        '/images/favicon/7.png',
        '/images/favicon/6.png',
        '/images/favicon/5.png',
        '/images/favicon/4.png',
        '/images/favicon/3.png',
        '/images/favicon/2.png',
        '/images/favicon/1.png',
        '/images/help.gif',
        '/images/donate.jpg',
        '/images/star.png',
        '/images/sun.png',
        '/images/hurry.png',
        '/images/timeup.png',
        '/images/og-icon.png',
        '/manifest.json',
        '/fonts/JuliusSansOne-Regular.ttf',
        '/fonts/Sacramento-Regular.ttf',
        '/fonts/Inconsolata.otf',
        '/css/awsome/fonts/FontAwesome.otf',
        '/css/awsome/fonts/fontawesome-webfont.eot',
        '/css/awsome/fonts/fontawesome-webfont.svg',
        '/css/awsome/fonts/fontawesome-webfont.ttf',
        '/css/awsome/fonts/fontawesome-webfont.woff',
        '/css/awsome/fonts/fontawesome-webfont.woff2',
        '/js/sweetalert.min.js',
        '/js/shortcut.js',
        '/js/script.js',
        '/js/particles.min.js',
        '/js/app.js',
        '/favicon.ico'

      ])
          .then(() => self.skipWaiting());
    })
  );
});

self.addEventListener('activate', event => {
  clearCache();
  event.waitUntil(self.clients.claim());
});

self.addEventListener('fetch', event => {
  event.respondWith(
    caches.open(cacheName)
      .then(cache => cache.match(event.request, {ignoreSearch: true}))
      .then(response => {
      return response || fetch(event.request);
    })
  );
});<|endoftext|>


<commit_before>self.addEventListener('push', function(event, data) {
    console.log(data);
  console.log('Received a push message', event);

  var title = event.title;
  var body = event.body;
  var icon = 'https://raw.githubusercontent.com/deanhume/typography/gh-pages/icons/typography.png';
  var tag = 'socialivo';

  event.waitUntil(
    self.registration.showNotification(title, {
       body: body,
       icon: icon,
       tag: tag
     })
   );
});

self.addEventListener('notificationclick', function(event) {
  console.log('On notification click: ', event.notification.tag);
  // Android doesn't close the notification when you click on it
  // See: http://crbug.com/463146
  event.notification.close();

  // This looks to see if the current is already open and
  // focuses if it is
  event.waitUntil(
    clients.matchAll({
      type: "window"
    })
    .then(function(clientList) {
      for (var i = 0; i < clientList.length; i++) {
        var client = clientList[i];
        if (client.url == '/' && 'focus' in client)
          return client.focus();
      }
      if (clients.openWindow) {
        return clients.openWindow('https://www.socialivo.com');
      }
    })
  );
});<commit_msg>service worker<commit_after>self.addEventListener('push', function(event) {
  // Since there is no payload data with the first version
  // of push messages, we'll grab some data from
  // an API and use it to populate a notification
  event.waitUntil(
    fetch(SOME_API_ENDPOINT).then(function(response) {
      if (response.status !== 200) {
        // Either show a message to the user explaining the error
        // or enter a generic message and handle the
        // onnotificationclick event to direct the user to a web page
        console.log('Looks like there was a problem. Status Code: ' + response.status);
        throw new Error();
      }

      // Examine the text in the response
      return response.json().then(function(data) {
          console.log(data);return;
        if (data.error || !data.notification) {
          console.error('The API returned an error.', data.error);
          throw new Error();
        }

        var title = data.notification.title;
        var message = data.notification.message;
        var icon = data.notification.icon;
        var notificationTag = data.notification.tag;

        return self.registration.showNotification(title, {
          body: message,
          icon: icon,
          tag: notificationTag
        });
      });
    }).catch(function(err) {
      console.error('Unable to retrieve data', err);

      var title = 'An error occurred';
      var message = 'We were unable to get the information for this push message';
      var icon = URL_TO_DEFAULT_ICON;
      var notificationTag = 'notification-error';
      return self.registration.showNotification(title, {
          body: message,
          icon: icon,
          tag: notificationTag
        });
    })
  );
});

self.addEventListener('notificationclick', function(event) {
  console.log('On notification click: ', event.notification.tag);
  // Android doesn't close the notification when you click on it
  // See: http://crbug.com/463146
  event.notification.close();

  // This looks to see if the current is already open and
  // focuses if it is
  event.waitUntil(
    clients.matchAll({
      type: "window"
    })
    .then(function(clientList) {
      for (var i = 0; i < clientList.length; i++) {
        var client = clientList[i];
        if (client.url == '/' && 'focus' in client)
          return client.focus();
      }
      if (clients.openWindow) {
        return clients.openWindow('https://www.socialivo.com');
      }
    })
  );
});<|endoftext|>


<commit_before>let doCache = false;

let CACHE_NAME = 'quickbill-cache-v1';

let urlsToCache = [
  '/',
  './assets/styles/css/styles.css',
  './assets/styles/images/*',
  './dist/index-bundle.js'
];

self.addEventListener("activate", event => {
    const cacheWhiteList = [CACHE_NAME];
    event.waitUntil(
        caches.keys()
            .then(keyList =>
                Promise.all(keyList.map(key => {
                    if (!cacheWhiteList.includes(key)) {
                        console.log('Deleting Cache: ' + key);
                        return caches.delete(key);
                    }
                }))
            )
    );
});

self.addEventListener("install", function(event) {
    if (doCache) {
        event.waitUntil(
            caches.open(CACHE_NAME)
                .then(function(cache) {
                    fetch("manifest.json")
                        .then(resp => {
                            resp.json();
                        })
                        .then(() => {
                            cache.addAll(urlsToCache);
                            console.log('cached');
                        })
                })
        )
    }
})

self.addEventListener('fetch', function(event) {
    if (doCache) {
      event.respondWith(
          caches.match(event.request).then(function(response) {
              return response || fetch(event.request);
          })
      );
    }
});<commit_msg>Fix source url to cache<commit_after>let doCache = false;

let CACHE_NAME = 'quickbill-cache-v1';

let urlsToCache = [
  '/',
  './assets/css/styles.css',
  './assets/images/*',
  './dist/index-bundle.js'
];

self.addEventListener("activate", event => {
    const cacheWhiteList = [CACHE_NAME];
    event.waitUntil(
        caches.keys()
            .then(keyList =>
                Promise.all(keyList.map(key => {
                    if (!cacheWhiteList.includes(key)) {
                        console.log('Deleting Cache: ' + key);
                        return caches.delete(key);
                    }
                }))
            )
    );
});

self.addEventListener("install", function(event) {
    if (doCache) {
        event.waitUntil(
            caches.open(CACHE_NAME)
                .then(function(cache) {
                    fetch("manifest.json")
                        .then(resp => {
                            resp.json();
                        })
                        .then(() => {
                            cache.addAll(urlsToCache);
                            console.log('cached');
                        })
                })
        )
    }
})

self.addEventListener('fetch', function(event) {
    if (doCache) {
      event.respondWith(
          caches.match(event.request).then(function(response) {
              return response || fetch(event.request);
          })
      );
    }
});<|endoftext|>


<commit_before>import React from 'react';
import { Margin } from 'styled-components-spacing';
import paramCase from 'param-case';
import { compose, graphql } from 'react-apollo';
import { set } from 'react-redux-values';
import { SubmissionError, reset, stopSubmit, startSubmit } from 'redux-form';
import ReduxForm from 'declarative-redux-form';
import { connect } from 'react-redux';
import find from 'lodash.find';
import get from 'lodash.get';
import intercept from 'apr-intercept';
import remcalc from 'remcalc';

import {
  ViewContainer,
  StatusLoader,
  Divider,
  H3,
  TagList
} from 'joyent-ui-toolkit';

import {
  default as Tag,
  AddForm as TagsAddForm,
  EditForm as TagsEditForm
} from '@components/instances/tags';

import GetTags from '@graphql/list-tags.gql';
import UpdateTags from '@graphql/update-tags.gql';
import DeleteTag from '@graphql/delete-tag.gql';
import Index from '@state/gen-index';
import parseError from '@state/parse-error';
import ToolbarForm from '@components/instances/toolbar';

const MENU_FORM_NAME = 'instance-tags-list-menu';
const ADD_FORM_NAME = 'instance-tags-add-new';
const EDIT_FORM_KEY = field => `instance-tags-${paramCase(field)}`;

export const Tags = ({
  tags = [],
  addOpen,
  editing,
  loading,
  handleToggleAddOpen,
  handleToggleEditing,
  handleCancel,
  handleEdit,
  handleRemove,
  handleCreate
}) => {
  const _loading = !(loading && !tags.length) ? null : <StatusLoader />;

  const _add = addOpen ? (
    <ReduxForm
      form={ADD_FORM_NAME}
      onSubmit={handleCreate}
      onCancel={() => handleToggleAddOpen(false)}
    >
      {TagsAddForm}
    </ReduxForm>
  ) : null;

  const _line = !_loading
    ? [
        <Divider key="line" height={remcalc(1)} />,
        <Divider key="after-line-space" height={remcalc(24)} transparent />
      ]
    : null;

  const _count = !_loading ? (
    <Margin bottom={4} top={addOpen && 4}>

{tags.length} tags

    </Margin>
  ) : null;

  const _tags = !_loading ? (
    <TagList>
      {tags.map(({ id, name, value }) => (
        <Tag
          key={id}
          id={id}
          name={name}
          value={value}
          onClick={!editing && (() => handleToggleEditing(name))}
        />
      ))}
    </TagList>
  ) : null;

  const _edit = editing ? (
    <ReduxForm
      form={editing.form}
      initialValues={{ name: editing.name, value: editing.value }}
      onSubmit={handleEdit}
    >
      {props => (
        <TagsEditForm
          {...props}
          /* yeah, we need this here too */
          initialValues={{ name: editing.name, value: editing.value }}
          onCancel={() => handleToggleEditing(false)}
          onToggleExpanded={() => handleToggleEditing(false)}
          onRemove={() => handleRemove(editing.form, editing)}
          removing={editing.removing}
        />
      )}
    </ReduxForm>
  ) : null;

  return (
    <ViewContainer main>
      <ReduxForm form={MENU_FORM_NAME}>
        {props => (
          <ToolbarForm
            {...props}
            searchable={!_loading}
            actionLabel="Add tag"
            actionable={!editing}
            onActionClick={() => handleToggleAddOpen(!addOpen)}
          />
        )}
      </ReduxForm>
      <Divider height={remcalc(11)} transparent />
      {_line}
      {_loading}
      {_add}
      {_edit}
      {_count}
      {_tags}
    </ViewContainer>
  );
};

export default compose(
  graphql(UpdateTags, { name: 'updateTags' }),
  graphql(DeleteTag, { name: 'deleteTag' }),
  graphql(GetTags, {
    options: ({ match }) => ({
      pollInterval: 1000,
      variables: {
        name: get(match, 'params.instance')
      }
    }),
    props: ({ data: { loading, error, variables, refetch, ...rest } }) => {
      const { name } = variables;

      const instance = find(get(rest, 'machines', []), ['name', name]);
      const tags = get(instance, 'tags', []);

      return {
        tags,
        instance,
        index: Index(tags),
        loading,
        error,
        refetch
      };
    }
  }),
  connect(
    ({ values, form }, { tags, index, ...ownProps }) => {
      // get search value
      const filter = get(form, `${MENU_FORM_NAME}.values.filter`, false);
      // if user is searching something, get items that match that query
      const filtered = filter
        ? index.search(filter).map(({ ref }) => find(tags, ['id', ref]))
        : tags;

      const editingTagName = get(values, 'editing-tag', null);
      const removingTagName = get(values, 'removing-tag', null);
      const editingTag =
        editingTagName && find(filtered, ['name', editingTagName]);
      const removingTag = editingTagName === removingTagName;

      return {
        ...ownProps,
        tags: filtered,
        addOpen: get(values, 'add-tags-open', false),
        editing: editingTag && {
          ...editingTag,
          removing: Boolean(removingTag),
          form: EDIT_FORM_KEY(editingTag.name)
        }
      };
    },
    (dispatch, ownProps) => {
      return {
        handleToggleAddOpen: value =>
          dispatch(set({ name: `add-tags-open`, value })),
        handleToggleEditing: value =>
          dispatch(set({ name: `editing-tag`, value })),
        handleEdit: async ({ name, value }, _, { form, initialValues }) => {
          const { instance, deleteTag, updateTags, refetch } = ownProps;

          // call mutations
          const [err] = await intercept(
            Promise.all([
              deleteTag({
                variables: {
                  id: instance.id,
                  name: initialValues.name
                }
              }),
              updateTags({
                variables: {
                  id: instance.id,
                  tags: [{ name, value }]
                }
              })
            ])
          );

          if (err) {
            // show mutation error
            throw new SubmissionError({
              _error: parseError(err)
            });
          }

          dispatch([
            set({ name: `editing-tag`, value: false }),
            reset(form),
            startSubmit(form)
          ]);

          // fetch tags again (even though we are polling)
          return refetch();
        },
        handleRemove: async (form, { name }) => {
          const { instance, deleteTag, refetch } = ownProps;

          dispatch([
            set({ name: `removing-tag`, value: name }),
            startSubmit(form)
          ]);

          // call mutation
          const [err] = await intercept(
            deleteTag({
              variables: {
                id: instance.id,
                name
              }
            })
          );

          if (err) {
            // show mutation error
            throw new SubmissionError({
              _error: parseError(err)
            });
          }

          dispatch([
            set({ name: `editing-tag`, value: false }),
            set({ name: `removing-tag`, value: false }),
            reset(form),
            startSubmit(form)
          ]);

          // fetch tags again (even though we are polling)
          return refetch();
        },
        handleCreate: async ({ name, value }) => {
          const { updateTags, instance, refetch } = ownProps;

          // call mutation
          const [err] = await intercept(
            updateTags({
              variables: {
                id: instance.id,
                tags: [{ name, value }]
              }
            })
          );

          if (err) {
            // show mutation error
            throw new SubmissionError({
              _error: parseError(err)
            });
          }

          dispatch([
            // reset create new tags form
            reset(ADD_FORM_NAME),
            stopSubmit(ADD_FORM_NAME),
            // close add form
            set({ name: `add-tags-open`, value: false })
          ]);

          // fetch tags again (even though we are polling)
          return refetch();
        }
      };
    }
  )
)(Tags);
<commit_msg>fix(my-joy-beta): prevent race conditions when editing tags<commit_after>import React from 'react';
import { Margin } from 'styled-components-spacing';
import paramCase from 'param-case';
import { compose, graphql } from 'react-apollo';
import { set } from 'react-redux-values';
import { SubmissionError, reset, stopSubmit, startSubmit } from 'redux-form';
import ReduxForm from 'declarative-redux-form';
import { connect } from 'react-redux';
import find from 'lodash.find';
import get from 'lodash.get';
import intercept from 'apr-intercept';
import remcalc from 'remcalc';

import {
  ViewContainer,
  StatusLoader,
  Divider,
  H3,
  TagList
} from 'joyent-ui-toolkit';

import {
  default as Tag,
  AddForm as TagsAddForm,
  EditForm as TagsEditForm
} from '@components/instances/tags';

import GetTags from '@graphql/list-tags.gql';
import UpdateTags from '@graphql/update-tags.gql';
import DeleteTag from '@graphql/delete-tag.gql';
import Index from '@state/gen-index';
import parseError from '@state/parse-error';
import ToolbarForm from '@components/instances/toolbar';

const MENU_FORM_NAME = 'instance-tags-list-menu';
const ADD_FORM_NAME = 'instance-tags-add-new';
const EDIT_FORM_KEY = field => `instance-tags-${paramCase(field)}`;

export const Tags = ({
  tags = [],
  addOpen,
  editing,
  editable,
  loading,
  handleToggleAddOpen,
  handleToggleEditing,
  handleCancel,
  handleEdit,
  handleRemove,
  handleCreate
}) => {
  const _loading = !(loading && !tags.length) ? null : <StatusLoader />;

  const _add = addOpen ? (
    <ReduxForm
      form={ADD_FORM_NAME}
      onSubmit={handleCreate}
      onCancel={() => handleToggleAddOpen(false)}
    >
      {TagsAddForm}
    </ReduxForm>
  ) : null;

  const _line = !_loading
    ? [
        <Divider key="line" height={remcalc(1)} />,
        <Divider key="after-line-space" height={remcalc(24)} transparent />
      ]
    : null;

  const _count = !_loading ? (
    <Margin bottom={4} top={addOpen && 4}>

{tags.length} tags

    </Margin>
  ) : null;

  const _tags = !_loading ? (
    <TagList>
      {tags.map(({ id, name, value }) => (
        <Tag
          key={id}
          id={id}
          name={name}
          value={value}
          onClick={editable && !editing && (() => handleToggleEditing(name))}
        />
      ))}
    </TagList>
  ) : null;

  const _edit = editing ? (
    <ReduxForm
      form={editing.form}
      initialValues={{ name: editing.name, value: editing.value }}
      onSubmit={handleEdit}
    >
      {props => (
        <TagsEditForm
          {...props}
          /* yeah, we need this here too */
          initialValues={{ name: editing.name, value: editing.value }}
          onCancel={() => handleToggleEditing(false)}
          onToggleExpanded={() => handleToggleEditing(false)}
          onRemove={() => handleRemove(editing.form, editing)}
          removing={editing.removing}
        />
      )}
    </ReduxForm>
  ) : null;

  return (
    <ViewContainer main>
      <ReduxForm form={MENU_FORM_NAME}>
        {props => (
          <ToolbarForm
            {...props}
            searchable={!_loading}
            actionLabel="Add tag"
            actionable={!editing}
            onActionClick={() => handleToggleAddOpen(!addOpen)}
          />
        )}
      </ReduxForm>
      <Divider height={remcalc(11)} transparent />
      {_line}
      {_loading}
      {_add}
      {_edit}
      {_count}
      {_tags}
    </ViewContainer>
  );
};

export default compose(
  graphql(UpdateTags, { name: 'updateTags' }),
  graphql(DeleteTag, { name: 'deleteTag' }),
  graphql(GetTags, {
    options: ({ match }) => ({
      pollInterval: 1000,
      variables: {
        name: get(match, 'params.instance')
      }
    }),
    props: ({ data: { loading, error, variables, refetch, ...rest } }) => {
      const { name } = variables;

      const instance = find(get(rest, 'machines', []), ['name', name]);
      const tags = get(instance, 'tags', []);

      return {
        tags,
        instance,
        index: Index(tags),
        loading,
        error,
        refetch
      };
    }
  }),
  connect(
    ({ values, form }, { tags, index, ...ownProps }) => {
      // get search value
      const filter = get(form, `${MENU_FORM_NAME}.values.filter`, false);
      // if user is searching something, get items that match that query
      const filtered = filter
        ? index.search(filter).map(({ ref }) => find(tags, ['id', ref]))
        : tags;

      const addOpen = get(values, 'add-tags-open', false);
      const editingTagName = get(values, 'editing-tag', null);
      const removingTagName = get(values, 'removing-tag', null);
      const removingTag = editingTagName === removingTagName;
      const editingTag =
        editingTagName && find(filtered, ['name', editingTagName]);

      return {
        ...ownProps,
        tags: filtered,
        addOpen,
        // are existing tags editable?
        editable: !addOpen && !editingTag,
        // if a tag is being edited, which one?
        editing: editingTag && {
          ...editingTag,
          removing: Boolean(removingTag),
          form: EDIT_FORM_KEY(editingTag.name)
        }
      };
    },
    (dispatch, ownProps) => {
      return {
        handleToggleAddOpen: value =>
          dispatch(set({ name: `add-tags-open`, value })),
        handleToggleEditing: value =>
          dispatch(set({ name: `editing-tag`, value })),
        handleEdit: async ({ name, value }, _, { form, initialValues }) => {
          const { instance, deleteTag, updateTags, refetch } = ownProps;

          const replaceTag = async () => {
            // we can't mutate in parallel because if the tag name is the
            // same we can have a race condition and remove after updating

            await updateTags({
              variables: {
                id: instance.id,
                tags: [{ name, value }]
              }
            });

            await deleteTag({
              variables: {
                id: instance.id,
                name: initialValues.name
              }
            });
          };

          const updateValue = async () => {
            await updateTags({
              variables: {
                id: instance.id,
                tags: [{ name, value }]
              }
            });
          };

          const mutation =
            initialValues.name === name ? updateValue : replaceTag;

          const [err] = await intercept(mutation());

          if (err) {
            // show mutation error
            throw new SubmissionError({
              _error: parseError(err)
            });
          }

          dispatch([
            set({ name: `editing-tag`, value: false }),
            reset(form),
            startSubmit(form)
          ]);

          // fetch tags again (even though we are polling)
          return refetch();
        },
        handleRemove: async (form, { name }) => {
          const { instance, deleteTag, refetch } = ownProps;

          dispatch([
            set({ name: `removing-tag`, value: name }),
            startSubmit(form)
          ]);

          // call mutation
          const [err] = await intercept(
            deleteTag({
              variables: {
                id: instance.id,
                name
              }
            })
          );

          if (err) {
            // show mutation error
            throw new SubmissionError({
              _error: parseError(err)
            });
          }

          dispatch([
            set({ name: `editing-tag`, value: false }),
            set({ name: `removing-tag`, value: false }),
            reset(form),
            startSubmit(form)
          ]);

          // fetch tags again (even though we are polling)
          return refetch();
        },
        handleCreate: async ({ name, value }) => {
          const { updateTags, instance, refetch } = ownProps;

          // call mutation
          const [err] = await intercept(
            updateTags({
              variables: {
                id: instance.id,
                tags: [{ name, value }]
              }
            })
          );

          if (err) {
            // show mutation error
            throw new SubmissionError({
              _error: parseError(err)
            });
          }

          dispatch([
            // reset create new tags form
            reset(ADD_FORM_NAME),
            stopSubmit(ADD_FORM_NAME),
            // close add form
            set({ name: `add-tags-open`, value: false })
          ]);

          // fetch tags again (even though we are polling)
          return refetch();
        }
      };
    }
  )
)(Tags);
<|endoftext|>


<commit_before>pref("extensions.imageforward.imageURLRegExp", "[^\?]+\.(jpg|png)(\?.*)?");
pref("extensions.imageforward.linkURLRegExp", "[^\?]+\.(jpg|png)(\?.*)?");
pref("extensions.imageforward.minHeight", 200);
pref("extensions.imageforward.minWidth", 300);

<commit_msg>Update regular expressions<commit_after>pref("extensions.imageforward.imageURLRegExp", "^[^#\?]+\.(jpg|png)((#|\?).*)?$");
pref("extensions.imageforward.linkURLRegExp", "^[^#\?]+\.(jpg|png)((#|\?).*)?$");
pref("extensions.imageforward.minHeight", 200);
pref("extensions.imageforward.minWidth", 300);

<|endoftext|>


<commit_before>const select = require(`unist-util-select`)
const path = require(`path`)
const isRelativeUrl = require(`is-relative-url`)
const _ = require(`lodash`)
const { responsiveSizes } = require(`gatsby-plugin-sharp`)
const Promise = require(`bluebird`)

// If the image is relative (not hosted elsewhere)
// 1. Find the image file
// 2. Find the image's size
// 3. Filter out any responsive image sizes that are greater than the image's width
// 4. Create the responsive images.
// 5. Set the html w/ aspect ratio helper.
module.exports = (
  { files, markdownNode, markdownAST, pathPrefix, getNode },
  pluginOptions
) => {
  const defaults = {
    maxWidth: 650,
    wrapperStyle: ``,
    backgroundColor: `white`,
  }
  const options = _.defaults(pluginOptions, defaults)

  const imageNodes = select(markdownAST, `image`)
  return Promise.all(
    imageNodes.map(
      node =>
        new Promise((resolve, reject) => {
          const fileType = node.url.slice(-3)

          // Ignore gifs as we can't process them,
          // svgs as they are already responsive by definition
          if (
            isRelativeUrl(node.url) &&
            fileType !== `gif` &&
            fileType !== `svg`
          ) {
            const imagePath = path.posix.join(
              getNode(markdownNode.parent).dir,
              node.url
            )
            const imageNode = _.find(files, file => {
              if (file && file.absolutePath) {
                return file.absolutePath === imagePath
              }
              return null
            })
            if (!imageNode || !imageNode.absolutePath) {
              return resolve()
            }

            responsiveSizes({
              file: imageNode,
              args: options,
            }).then(responsiveSizesResult => {
              // console.log("responsiveSizesResult", responsiveSizesResult)
              // Calculate the paddingBottom %
              const ratio = `${1 / responsiveSizesResult.aspectRatio * 100}%`

              const originalImg = responsiveSizesResult.originalImage
              const fallbackSrc = responsiveSizesResult.src
              const srcSet = responsiveSizesResult.srcSet

              // TODO
              // add support for sub-plugins having a gatsby-node.js so can add a
              // bit of js/css to add blurry fade-in.
              // https://www.perpetual-beta.org/weblog/silky-smooth-image-loading.html
              //
              // TODO make linking to original image optional.

              // Construct new image node w/ aspect ratio placeholder
              const rawHTML = `
          <a
            class="gatsby-resp-image-link"
            href="${originalImg}"
            style="display: block"
            target="_blank"
          >
            <span
              class="gatsby-resp-image-wrapper"
              style="position: relative; z-index: -1; display: block; ${options.wrapperStyle}"
            >
              <span
                class="gatsby-resp-image-background-image"
                style="padding-bottom: ${ratio};position: relative; width: 100%; bottom: 0; left: 0; background-image: url('${responsiveSizesResult.base64}'); background-size: cover; display: block;"
              >
                <img
                  class="gatsby-resp-image-image"
                  style="width: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px ${options.backgroundColor};"
                  alt="${node.alt ? node.alt : ``}"
                  title="${node.title ? node.title : ``}"
                  src="${fallbackSrc}"
                  srcset="${srcSet}"
                  sizes="${responsiveSizesResult.sizes}"
                />
              </span>
            </span>
          </a>
          `

              // Replace the image node with an inline HTML node.
              node.type = `html`
              node.value = rawHTML
              return resolve()
            })
          } else {
            // Image isn't relative so there's nothing for us to do.
            return resolve()
          }
        })
    )
  )
}
<commit_msg>Open external image link with rel='noopener' (#1227)<commit_after>const select = require(`unist-util-select`)
const path = require(`path`)
const isRelativeUrl = require(`is-relative-url`)
const _ = require(`lodash`)
const { responsiveSizes } = require(`gatsby-plugin-sharp`)
const Promise = require(`bluebird`)

// If the image is relative (not hosted elsewhere)
// 1. Find the image file
// 2. Find the image's size
// 3. Filter out any responsive image sizes that are greater than the image's width
// 4. Create the responsive images.
// 5. Set the html w/ aspect ratio helper.
module.exports = (
  { files, markdownNode, markdownAST, pathPrefix, getNode },
  pluginOptions
) => {
  const defaults = {
    maxWidth: 650,
    wrapperStyle: ``,
    backgroundColor: `white`,
  }
  const options = _.defaults(pluginOptions, defaults)

  const imageNodes = select(markdownAST, `image`)
  return Promise.all(
    imageNodes.map(
      node =>
        new Promise((resolve, reject) => {
          const fileType = node.url.slice(-3)

          // Ignore gifs as we can't process them,
          // svgs as they are already responsive by definition
          if (
            isRelativeUrl(node.url) &&
            fileType !== `gif` &&
            fileType !== `svg`
          ) {
            const imagePath = path.posix.join(
              getNode(markdownNode.parent).dir,
              node.url
            )
            const imageNode = _.find(files, file => {
              if (file && file.absolutePath) {
                return file.absolutePath === imagePath
              }
              return null
            })
            if (!imageNode || !imageNode.absolutePath) {
              return resolve()
            }

            responsiveSizes({
              file: imageNode,
              args: options,
            }).then(responsiveSizesResult => {
              // console.log("responsiveSizesResult", responsiveSizesResult)
              // Calculate the paddingBottom %
              const ratio = `${1 / responsiveSizesResult.aspectRatio * 100}%`

              const originalImg = responsiveSizesResult.originalImage
              const fallbackSrc = responsiveSizesResult.src
              const srcSet = responsiveSizesResult.srcSet

              // TODO
              // add support for sub-plugins having a gatsby-node.js so can add a
              // bit of js/css to add blurry fade-in.
              // https://www.perpetual-beta.org/weblog/silky-smooth-image-loading.html
              //
              // TODO make linking to original image optional.

              // Construct new image node w/ aspect ratio placeholder
              const rawHTML = `
          <a
            class="gatsby-resp-image-link"
            href="${originalImg}"
            style="display: block"
            target="_blank"
            rel="noopener"
          >
            <span
              class="gatsby-resp-image-wrapper"
              style="position: relative; z-index: -1; display: block; ${options.wrapperStyle}"
            >
              <span
                class="gatsby-resp-image-background-image"
                style="padding-bottom: ${ratio};position: relative; width: 100%; bottom: 0; left: 0; background-image: url('${responsiveSizesResult.base64}'); background-size: cover; display: block;"
              >
                <img
                  class="gatsby-resp-image-image"
                  style="width: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px ${options.backgroundColor};"
                  alt="${node.alt ? node.alt : ``}"
                  title="${node.title ? node.title : ``}"
                  src="${fallbackSrc}"
                  srcset="${srcSet}"
                  sizes="${responsiveSizesResult.sizes}"
                />
              </span>
            </span>
          </a>
          `

              // Replace the image node with an inline HTML node.
              node.type = `html`
              node.value = rawHTML
              return resolve()
            })
          } else {
            // Image isn't relative so there's nothing for us to do.
            return resolve()
          }
        })
    )
  )
}
<|endoftext|>


<commit_before>/* eslint-env browser */

/* global ciscospark */

/* eslint-disable camelcase */
/* eslint-disable max-nested-callbacks */
/* eslint-disable no-alert */
/* eslint-disable no-console */
/* eslint-disable require-jsdoc */

// Declare some globals that we'll need throughout
let spark, meeting, meetings;

// First, let's wire our form fields up to localStorage so we don't have to
// retype things everytime we reload the page.

[
  'access-token'
  // 'invitee'
].forEach((id) => {
  const el = document.getElementById(id);
  el.value = localStorage.getItem(id);
  el.addEventListener('change', (event) => {
    localStorage.setItem(id, event.target.value);
  });
});

function connect() {
  if (!spark) {
    spark = ciscospark.init({
      config: {
        // Any other sdk config we need
      },
      credentials: {
        access_token: document.getElementById('access-token').value
      }
    });
  }

  if (!spark.internal.device.registered) {
    spark.internal.device
      .register()
      .then(() => {
        // This is just a little helper for our selenium tests and doesn't
        // really matter for the example
        document.body.classList.add('listening');
        document.getElementById('connection-status').innerHTML = 'connected';
        // return this.spark.internal.device.register()
        return spark.internal.mercury.connect();
      })
      // This is a terrible way to handle errors, but anything more specific is
      // going to depend a lot on your app
      .catch((err) => {
        console.error(err);
        // we'll rethrow here since we didn't really *handle* the error, we just
        // reported it
        throw err;
      });
  }
}

function startMeeting(m) {
  meeting = m;
  meeting.on('media:ready', (media) => {
    if (media.type === 'local') {
      document.getElementById('localvideo').srcObject = media.stream;
    }
    if (media.type === 'remote') {
      document.getElementById('remotevideo').srcObject = media.stream;
    }
  });
  meeting.on('media:stopped', (media) => {
    if (media.type === 'local') {
      document.getElementById('localvideo').srcObject = null;
    }

    if (media.type === 'remote') {
      document.getElementById('remotevideo').srcObject = null;
    }
  });
}

// In order to simplify the state management needed to keep track of our button
// handlers, we'll rely on the current call global object and only hook up event
// handlers once.

document.getElementById('meetings-object').addEventListener('click', () => {
  if (meetings) {
    console.info(meetings);
  }
});

document.getElementById('leave').addEventListener('click', () => {
  if (meeting) {
    meeting.leave();
  }
});

document.getElementById('start-sending-audio').addEventListener('click', () => {
  if (meeting) {
    meeting
      .unmuteAudio()
      .then((res) => {
        console.log('audio unmute resolved', res);
      })
      .catch((rej) => {
        console.log('audio unmute rejected', rej);
      });
  }
});

document.getElementById('stop-sending-audio').addEventListener('click', () => {
  if (meeting) {
    meeting
      .muteAudio()
      .then((res) => {
        console.log('audio mute resolved', res);
      })
      .catch((rej) => {
        console.log('audio mute rejected', rej);
      });
  }
});

document.getElementById('start-sending-video').addEventListener('click', () => {
  if (meeting) {
    meeting
      .unmuteVideo()
      .then((res) => {
        console.log('video unmute resolved', res);
      })
      .catch((rej) => {
        console.log('video unmute rejected', rej);
      });
  }
});

document.getElementById('stop-sending-video').addEventListener('click', () => {
  if (meeting) {
    meeting
      .muteVideo()
      .then((res) => {
        console.log('video mute resolved', res);
      })
      .catch((rej) => {
        console.log('video mute rejected', rej);
      });
  }
});
document.getElementById('start-receiving-audio').addEventListener('click', () => {
  if (meeting) {
    meeting.startReceivingAudio();
  }
});

document.getElementById('stop-receiving-audio').addEventListener('click', () => {
  if (meeting) {
    meeting.stopReceivingAudio();
  }
});

document.getElementById('start-receiving-video').addEventListener('click', () => {
  if (meeting) {
    meeting.startReceivingVideo();
  }
});

document.getElementById('stop-receiving-video').addEventListener('click', () => {
  if (meeting) {
    meeting.stopReceivingVideo();
  }
});

document.getElementById('getStatsRaw').addEventListener('click', () => {
  if (meeting) {
    meeting
      .getStats()
      .withId()
      .withMedia()
      .build()
      .getMedia()
      .withId()
      .withStream(meeting.mediaPeerConnection)
      .build()
      .getStream()
      .on('data', (raw) => {
        console.log(raw);
      });
  }
});

document.getElementById('getStatsFilter').addEventListener('click', () => {
  if (meeting) {
    // meeting
    //   .getStats()
    //   .withMedia()
    //   .build()
    //   .getMedia()
    //   .withId()
    //   .withEvents()
    //   .withHistory()
    //   .withAggregator()
    //   .withInterval()
    //   .withFilter(meeting.mediaPeerConnection)
    //   .build()
    //   .onData((filtered) => {
    //     console.log(filtered);
    //   });
    // OR
    // const stats = meeting.getStats().withId().withMedia().build();
    // const media = stats.getMedia()...
    // media.doData((filtered) => {
    //  do stuff
    // })
    // OR
    meeting.getStats({
      media: {
        id: true,
        history: true,
        interval: true,
        aggregator: true,
        filter: meeting.mediaPeerConnection,
        events: true
      }
    });
    meeting
      .getStats()
      .getMedia()
      .onData((filtered) => {
        console.log(filtered);
      });
    meeting
      .getStats()
      .getMedia()
      .getEvents()
      .on('stats:update', (stats) => {
        console.log(stats);
      });
  }
});

document.getElementById('getAggregation').addEventListener('click', () => {
  if (meeting) {
    console.log(
      meeting
        .getStats()
        .getMedia()
        .getAggregator()
        .get()
    );
  }
});

document.getElementById('getHistory').addEventListener('click', () => {
  if (meeting) {
    console.log(
      meeting
        .getStats()
        .getMedia()
        .getHistory()
        .get()
    );
  }
});

document.getElementById('getInterval').addEventListener('click', () => {
  if (meeting) {
    console.log(
      meeting
        .getStats()
        .getMedia()
        .getInterval()
        .get()
    );
  }
});

// Now, let's set up incoming call handling
document.getElementById('credentials').addEventListener('submit', (event) => {
  // let's make sure we don't reload the page when we submit the form
  event.preventDefault();
  connect();
});

document.getElementById('PMR').addEventListener('submit', (event) => {
  event.preventDefault();
  return spark.meetings.personalMeetingRoom.get().then((pmr) => {
    console.log(pmr);
    document.getElementById('pmr-sip').value = spark.meetings.personalMeetingRoom.sipUri;
    return spark.meetings.create(document.getElementById('pmr-sip').value).then((m) => {
      startMeeting(m);
    });
  });
});

// And finally, let's wire up dialing
document.getElementById('dialer').addEventListener('submit', (event) => {
  // again, we don't want to reload when we try to dial
  event.preventDefault();
  return spark.meetings.create(document.getElementById('invitee').value).then((m) => {
    console.error(m);
    startMeeting(m);
  });
});

// And finally, let's wire up dialing
document.getElementById('conversation-dialer').addEventListener('submit', (event) => {
  // again, we don't want to reload when we try to dial
  event.preventDefault();
  return spark.meetings.create(document.getElementById('invitee-convo').value).then((m) => {
    console.error(m);
    startMeeting(m);
  });
});

// And finally, let's wire up dialing
document.getElementById('type-dialer').addEventListener('submit', (event) => {
  // again, we don't want to reload when we try to dial
  event.preventDefault();
  return spark.meetings.createWithType(document.getElementById('type').value, document.getElementById('value')).then((m) => {
    console.error(m);
    startMeeting(m);
  });
});

// And finally, let's wire up dialing
// document.getElementById('getmedia').addEventListener('click', () => {
//   // again, we don't want to reload when we try to dial
//   // const streams = media.getUserMedia();
//   return media.getUser();
// });
// And finally, let's wire up dialing
document.getElementById('join').addEventListener('click', () => meeting.join());
<commit_msg>feat(meeting): cr cmts<commit_after>/* eslint-env browser */

/* global ciscospark */

/* eslint-disable camelcase */
/* eslint-disable max-nested-callbacks */
/* eslint-disable no-alert */
/* eslint-disable no-console */
/* eslint-disable require-jsdoc */

// Declare some globals that we'll need throughout
let spark, meeting, meetings;

// First, let's wire our form fields up to localStorage so we don't have to
// retype things everytime we reload the page.

[
  'access-token'
  // 'invitee'
].forEach((id) => {
  const el = document.getElementById(id);
  el.value = localStorage.getItem(id);
  el.addEventListener('change', (event) => {
    localStorage.setItem(id, event.target.value);
  });
});

function connect() {
  if (!spark) {
    spark = ciscospark.init({
      config: {
        // Any other sdk config we need
      },
      credentials: {
        access_token: document.getElementById('access-token').value
      }
    });
  }

  if (!spark.internal.device.registered) {
    spark.internal.device
      .register()
      .then(() => {
        // This is just a little helper for our selenium tests and doesn't
        // really matter for the example
        document.body.classList.add('listening');
        document.getElementById('connection-status').innerHTML = 'connected';
        // return this.spark.internal.device.register()
        return spark.internal.mercury.connect();
      })
      // This is a terrible way to handle errors, but anything more specific is
      // going to depend a lot on your app
      .catch((err) => {
        console.error(err);
        // we'll rethrow here since we didn't really *handle* the error, we just
        // reported it
        throw err;
      });
  }
}

function startMeeting(m) {
  meeting = m;
  meeting.on('media:ready', (media) => {
    if (media.type === 'local') {
      document.getElementById('localvideo').srcObject = media.stream;
    }
    if (media.type === 'remote') {
      document.getElementById('remotevideo').srcObject = media.stream;
    }
  });
  meeting.on('media:stopped', (media) => {
    if (media.type === 'local') {
      document.getElementById('localvideo').srcObject = null;
    }

    if (media.type === 'remote') {
      document.getElementById('remotevideo').srcObject = null;
    }
  });
}

// In order to simplify the state management needed to keep track of our button
// handlers, we'll rely on the current call global object and only hook up event
// handlers once.

document.getElementById('meetings-object').addEventListener('click', () => {
  if (meetings) {
    console.info(meetings);
  }
});

document.getElementById('leave').addEventListener('click', () => {
  if (meeting) {
    meeting.leave();
  }
});

document.getElementById('start-sending-audio').addEventListener('click', () => {
  if (meeting) {
    meeting
      .unmuteAudio()
      .then((res) => {
        console.log('audio unmute resolved', res);
      })
      .catch((rej) => {
        console.log('audio unmute rejected', rej);
      });
  }
});

document.getElementById('stop-sending-audio').addEventListener('click', () => {
  if (meeting) {
    meeting
      .muteAudio()
      .then((res) => {
        console.log('audio mute resolved', res);
      })
      .catch((rej) => {
        console.log('audio mute rejected', rej);
      });
  }
});

document.getElementById('start-sending-video').addEventListener('click', () => {
  if (meeting) {
    meeting
      .unmuteVideo()
      .then((res) => {
        console.log('video unmute resolved', res);
      })
      .catch((rej) => {
        console.log('video unmute rejected', rej);
      });
  }
});

document.getElementById('stop-sending-video').addEventListener('click', () => {
  if (meeting) {
    meeting
      .muteVideo()
      .then((res) => {
        console.log('video mute resolved', res);
      })
      .catch((rej) => {
        console.log('video mute rejected', rej);
      });
  }
});
document.getElementById('start-receiving-audio').addEventListener('click', () => {
  if (meeting) {
    meeting.startReceivingAudio();
  }
});

document.getElementById('stop-receiving-audio').addEventListener('click', () => {
  if (meeting) {
    meeting.stopReceivingAudio();
  }
});

document.getElementById('start-receiving-video').addEventListener('click', () => {
  if (meeting) {
    meeting.startReceivingVideo();
  }
});

document.getElementById('stop-receiving-video').addEventListener('click', () => {
  if (meeting) {
    meeting.stopReceivingVideo();
  }
});

document.getElementById('getStatsRaw').addEventListener('click', () => {
  if (meeting) {
    meeting
      .getStats()
      .withId()
      .withMedia()
      .build()
      .getMedia()
      .withId()
      .withStream(meeting.mediaPeerConnection)
      .build()
      .getStream()
      .on('data', (raw) => {
        console.log(raw);
      });
  }
});

document.getElementById('getStatsFilter').addEventListener('click', () => {
  if (meeting) {
    // meeting
    //   .getStats()
    //   .withMedia()
    //   .build()
    //   .getMedia()
    //   .withId()
    //   .withEvents()
    //   .withHistory()
    //   .withAggregator()
    //   .withInterval()
    //   .withFilter(meeting.mediaPeerConnection)
    //   .build()
    //   .onData((filtered) => {
    //     console.log(filtered);
    //   });
    // OR
    // const stats = meeting.getStats().withId().withMedia().build();
    // const media = stats.getMedia()...
    // media.doData((filtered) => {
    //  do stuff
    // })
    // OR
    meeting.getStats({
      media: {
        id: true,
        history: true,
        interval: true,
        aggregator: true,
        filter: meeting.mediaPeerConnection,
        events: true
      }
    });
    meeting
      .getStats()
      .getMedia()
      .onData((filtered) => {
        console.log(filtered);
      });
    meeting
      .getStats()
      .getMedia()
      .getEvents()
      .on('stats:update', (stats) => {
        console.log(stats);
      });
  }
});

document.getElementById('getAggregation').addEventListener('click', () => {
  if (meeting) {
    console.log(
      meeting
        .getStats()
        .getMedia()
        .getAggregator()
        .get()
    );
  }
});

document.getElementById('getHistory').addEventListener('click', () => {
  if (meeting) {
    console.log(
      meeting
        .getStats()
        .getMedia()
        .getHistory()
        .get()
    );
  }
});

document.getElementById('getInterval').addEventListener('click', () => {
  if (meeting) {
    console.log(
      meeting
        .getStats()
        .getMedia()
        .getInterval()
        .get()
    );
  }
});

// Now, let's set up incoming call handling
document.getElementById('credentials').addEventListener('submit', (event) => {
  // let's make sure we don't reload the page when we submit the form
  event.preventDefault();
  connect();
});

document.getElementById('PMR').addEventListener('submit', (event) => {
  event.preventDefault();
  return spark.meetings.personalMeetingRoom.get().then((pmr) => {
    console.log(pmr);
    document.getElementById('pmr-sip').value = spark.meetings.personalMeetingRoom.sipUri;
    return spark.meetings.create(document.getElementById('pmr-sip').value).then((m) => {
      startMeeting(m);
    });
  });
});

// And finally, let's wire up dialing
document.getElementById('dialer').addEventListener('submit', (event) => {
  // again, we don't want to reload when we try to dial
  event.preventDefault();
  return spark.meetings.create(document.getElementById('invitee').value).then((m) => {
    console.log(m);
    startMeeting(m);
  });
});

// And finally, let's wire up dialing
document.getElementById('conversation-dialer').addEventListener('submit', (event) => {
  // again, we don't want to reload when we try to dial
  event.preventDefault();
  return spark.meetings.create(document.getElementById('invitee-convo').value).then((m) => {
    console.log(m);
    startMeeting(m);
  });
});

// And finally, let's wire up dialing
document.getElementById('type-dialer').addEventListener('submit', (event) => {
  // again, we don't want to reload when we try to dial
  event.preventDefault();
  return spark.meetings.createWithType(document.getElementById('type').value, document.getElementById('value')).then((m) => {
    console.log(m);
    startMeeting(m);
  });
});

// And finally, let's wire up dialing
// document.getElementById('getmedia').addEventListener('click', () => {
//   // again, we don't want to reload when we try to dial
//   // const streams = media.getUserMedia();
//   return media.getUser();
// });
// And finally, let's wire up dialing
document.getElementById('join').addEventListener('click', () => meeting.join());
<|endoftext|>


<commit_before>pref("extensions.imageforward.imageURLRegExp", "^[^#\?]+\.(jpg|png)((#|\?).*)?$");
pref("extensions.imageforward.linkURLRegExp", "^[^#\?]+\.(jpg|png)((#|\?).*)?$");
pref("extensions.imageforward.minHeight", 200);
pref("extensions.imageforward.minWidth", 300);

<commit_msg>Add jpeg to regular expressions<commit_after>pref("extensions.imageforward.imageURLRegExp", "^[^#\?]+\.(jpg|jpeg|png)((#|\?).*)?$");
pref("extensions.imageforward.linkURLRegExp", "^[^#\?]+\.(jpg|jpeg|png)((#|\?).*)?$");
pref("extensions.imageforward.minHeight", 200);
pref("extensions.imageforward.minWidth", 300);

<|endoftext|>


<commit_before>const chokidar = require(`chokidar`)
const fs = require(`fs`)
const path = require(`path`)
const { Machine, interpret } = require(`xstate`)

const { createFileNode } = require(`./create-file-node`)

/**
 * Create a state machine to manage Chokidar's not-ready/ready states.
 */
const createFSMachine = (
  { actions: { createNode, deleteNode }, getNode, createNodeId, reporter },
  pluginOptions
) => {
  const createAndProcessNode = path => {
    const fileNodePromise = createFileNode(
      path,
      createNodeId,
      pluginOptions
    ).then(fileNode => {
      createNode(fileNode)
      return null
    })
    return fileNodePromise
  }

  const deletePathNode = path => {
    const node = getNode(createNodeId(path))
    // It's possible the node was never created as sometimes tools will
    // write and then immediately delete temporary files to the file system.
    if (node) {
      deleteNode({ node })
    }
  }

  // For every path that is reported before the 'ready' event, we throw them
  // into a queue and then flush the queue when 'ready' event arrives.
  // After 'ready', we handle the 'add' event without putting it into a queue.
  let pathQueue = []
  const flushPathQueue = () => {
    let queue = pathQueue.slice()
    pathQueue = null
    return Promise.all(
      // eslint-disable-next-line consistent-return
      queue.map(({ op, path }) => {
        switch (op) {
          case `delete`:
            return deletePathNode(path)
          case `upsert`:
            return createAndProcessNode(path)
        }
      })
    )
  }

  const fsMachine = Machine(
    {
      id: `fs`,
      type: `parallel`,
      states: {
        BOOTSTRAP: {
          initial: `BOOTSTRAPPING`,
          states: {
            BOOTSTRAPPING: {
              on: {
                BOOTSTRAP_FINISHED: `BOOTSTRAPPED`,
              },
            },
            BOOTSTRAPPED: {
              type: `final`,
            },
          },
        },
        CHOKIDAR: {
          initial: `NOT_READY`,
          states: {
            NOT_READY: {
              on: {
                CHOKIDAR_READY: `READY`,
                CHOKIDAR_ADD: { actions: `queueNodeProcessing` },
                CHOKIDAR_CHANGE: { actions: `queueNodeProcessing` },
                CHOKIDAR_UNLINK: { actions: `queueNodeDeleting` },
              },
              exit: `flushPathQueue`,
            },
            READY: {
              on: {
                CHOKIDAR_ADD: { actions: `createAndProcessNode` },
                CHOKIDAR_CHANGE: { actions: `createAndProcessNode` },
                CHOKIDAR_UNLINK: { actions: `deletePathNode` },
              },
            },
          },
        },
      },
    },
    {
      actions: {
        createAndProcessNode(_, { pathType, path }, { state }) {
          if (state.matches(`BOOTSTRAP.BOOTSTRAPPED`)) {
            reporter.info(`added ${pathType} at ${path}`)
          }
          createAndProcessNode(path).catch(err => reporter.error(err))
        },
        deletePathNode(_, { pathType, path }, { state }) {
          if (state.matches(`BOOTSTRAP.BOOTSTRAPPED`)) {
            reporter.info(`${pathType} deleted at ${path}`)
          }
          deletePathNode(path)
        },
        flushPathQueue(_, { resolve, reject }) {
          flushPathQueue().then(resolve, reject)
        },
        queueNodeDeleting(_, { path }) {
          pathQueue.push({ op: `delete`, path })
        },
        queueNodeProcessing(_, { path }) {
          pathQueue.push({ op: `upsert`, path })
        },
      },
    }
  )
  return interpret(fsMachine).start()
}

exports.sourceNodes = (api, pluginOptions) => {
  // Validate that the path exists.
  if (!fs.existsSync(pluginOptions.path)) {
    api.reporter.panic(`
The path passed to gatsby-source-filesystem does not exist on your file system:

${pluginOptions.path}

Please pick a path to an existing directory.

See docs here - https://www.gatsbyjs.org/packages/gatsby-source-filesystem/
      `)
  }

  // Validate that the path is absolute.
  // Absolute paths are required to resolve images correctly.
  if (!path.isAbsolute(pluginOptions.path)) {
    pluginOptions.path = path.resolve(process.cwd(), pluginOptions.path)
  }

  const fsMachine = createFSMachine(api, pluginOptions)

  // Once bootstrap is finished, we only let one File node update go through
  // the system at a time.
  api.emitter.on(`BOOTSTRAP_FINISHED`, () => {
    fsMachine.send(`BOOTSTRAP_FINISHED`)
  })

  const watcher = chokidar.watch(pluginOptions.path, {
    ignored: [
      `**/*.un~`,
      `**/.DS_Store`,
      `**/.gitignore`,
      `**/.npmignore`,
      `**/.babelrc`,
      `**/yarn.lock`,
      `**/bower_components`,
      `**/node_modules`,
      `../**/dist/**`,
      ...(pluginOptions.ignore || []),
    ],
  })

  watcher.on(`add`, path => {
    fsMachine.send({ type: `CHOKIDAR_ADD`, pathType: `file`, path })
  })

  watcher.on(`change`, path => {
    fsMachine.send({ type: `CHOKIDAR_CHANGE`, pathType: `file`, path })
  })

  watcher.on(`unlink`, path => {
    fsMachine.send({ type: `CHOKIDAR_UNLINK`, pathType: `file`, path })
  })

  watcher.on(`addDir`, path => {
    fsMachine.send({ type: `CHOKIDAR_ADD`, pathType: `directory`, path })
  })

  watcher.on(`unlinkDir`, path => {
    fsMachine.send({ type: `CHOKIDAR_UNLINK`, pathType: `directory`, path })
  })

  return new Promise((resolve, reject) => {
    watcher.on(`ready`, () => {
      fsMachine.send({ type: `CHOKIDAR_READY`, resolve, reject })
    })
  })
}

exports.setFieldsOnGraphQLNodeType = require(`./extend-file-node`)
<commit_msg>fix(gatsby-source-filesystem): fix add/change text (#20188)<commit_after>const chokidar = require(`chokidar`)
const fs = require(`fs`)
const path = require(`path`)
const { Machine, interpret } = require(`xstate`)

const { createFileNode } = require(`./create-file-node`)

/**
 * Create a state machine to manage Chokidar's not-ready/ready states.
 */
const createFSMachine = (
  { actions: { createNode, deleteNode }, getNode, createNodeId, reporter },
  pluginOptions
) => {
  const createAndProcessNode = path => {
    const fileNodePromise = createFileNode(
      path,
      createNodeId,
      pluginOptions
    ).then(fileNode => {
      createNode(fileNode)
      return null
    })
    return fileNodePromise
  }

  const deletePathNode = path => {
    const node = getNode(createNodeId(path))
    // It's possible the node was never created as sometimes tools will
    // write and then immediately delete temporary files to the file system.
    if (node) {
      deleteNode({ node })
    }
  }

  // For every path that is reported before the 'ready' event, we throw them
  // into a queue and then flush the queue when 'ready' event arrives.
  // After 'ready', we handle the 'add' event without putting it into a queue.
  let pathQueue = []
  const flushPathQueue = () => {
    let queue = pathQueue.slice()
    pathQueue = null
    return Promise.all(
      // eslint-disable-next-line consistent-return
      queue.map(({ op, path }) => {
        switch (op) {
          case `delete`:
            return deletePathNode(path)
          case `upsert`:
            return createAndProcessNode(path)
        }
      })
    )
  }

  const log = expr => (ctx, action, meta) => {
    if (meta.state.matches(`BOOTSTRAP.BOOTSTRAPPED`)) {
      reporter.info(expr(ctx, action, meta))
    }
  }

  const fsMachine = Machine(
    {
      id: `fs`,
      type: `parallel`,
      states: {
        BOOTSTRAP: {
          initial: `BOOTSTRAPPING`,
          states: {
            BOOTSTRAPPING: {
              on: {
                BOOTSTRAP_FINISHED: `BOOTSTRAPPED`,
              },
            },
            BOOTSTRAPPED: {
              type: `final`,
            },
          },
        },
        CHOKIDAR: {
          initial: `NOT_READY`,
          states: {
            NOT_READY: {
              on: {
                CHOKIDAR_READY: `READY`,
                CHOKIDAR_ADD: { actions: `queueNodeProcessing` },
                CHOKIDAR_CHANGE: { actions: `queueNodeProcessing` },
                CHOKIDAR_UNLINK: { actions: `queueNodeDeleting` },
              },
              exit: `flushPathQueue`,
            },
            READY: {
              on: {
                CHOKIDAR_ADD: {
                  actions: [
                    `createAndProcessNode`,
                    log(
                      (_, { pathType, path }) => `added ${pathType} at ${path}`
                    ),
                  ],
                },
                CHOKIDAR_CHANGE: {
                  actions: [
                    `createAndProcessNode`,
                    log(
                      (_, { pathType, path }) =>
                        `changed ${pathType} at ${path}`
                    ),
                  ],
                },
                CHOKIDAR_UNLINK: {
                  actions: [
                    `deletePathNode`,
                    log(
                      (_, { pathType, path }) =>
                        `deleted ${pathType} at ${path}`
                    ),
                  ],
                },
              },
            },
          },
        },
      },
    },
    {
      actions: {
        createAndProcessNode(_, { pathType, path }) {
          createAndProcessNode(path).catch(err => reporter.error(err))
        },
        deletePathNode(_, { pathType, path }, { state }) {
          deletePathNode(path)
        },
        flushPathQueue(_, { resolve, reject }) {
          flushPathQueue().then(resolve, reject)
        },
        queueNodeDeleting(_, { path }) {
          pathQueue.push({ op: `delete`, path })
        },
        queueNodeProcessing(_, { path }) {
          pathQueue.push({ op: `upsert`, path })
        },
      },
    }
  )
  return interpret(fsMachine).start()
}

exports.sourceNodes = (api, pluginOptions) => {
  // Validate that the path exists.
  if (!fs.existsSync(pluginOptions.path)) {
    api.reporter.panic(`
The path passed to gatsby-source-filesystem does not exist on your file system:
${pluginOptions.path}
Please pick a path to an existing directory.
See docs here - https://www.gatsbyjs.org/packages/gatsby-source-filesystem/
      `)
  }

  // Validate that the path is absolute.
  // Absolute paths are required to resolve images correctly.
  if (!path.isAbsolute(pluginOptions.path)) {
    pluginOptions.path = path.resolve(process.cwd(), pluginOptions.path)
  }

  const fsMachine = createFSMachine(api, pluginOptions)

  // Once bootstrap is finished, we only let one File node update go through
  // the system at a time.
  api.emitter.on(`BOOTSTRAP_FINISHED`, () => {
    fsMachine.send(`BOOTSTRAP_FINISHED`)
  })

  const watcher = chokidar.watch(pluginOptions.path, {
    ignored: [
      `**/*.un~`,
      `**/.DS_Store`,
      `**/.gitignore`,
      `**/.npmignore`,
      `**/.babelrc`,
      `**/yarn.lock`,
      `**/bower_components`,
      `**/node_modules`,
      `../**/dist/**`,
      ...(pluginOptions.ignore || []),
    ],
  })

  watcher.on(`add`, path => {
    fsMachine.send({ type: `CHOKIDAR_ADD`, pathType: `file`, path })
  })

  watcher.on(`change`, path => {
    fsMachine.send({ type: `CHOKIDAR_CHANGE`, pathType: `file`, path })
  })

  watcher.on(`unlink`, path => {
    fsMachine.send({ type: `CHOKIDAR_UNLINK`, pathType: `file`, path })
  })

  watcher.on(`addDir`, path => {
    fsMachine.send({ type: `CHOKIDAR_ADD`, pathType: `directory`, path })
  })

  watcher.on(`unlinkDir`, path => {
    fsMachine.send({ type: `CHOKIDAR_UNLINK`, pathType: `directory`, path })
  })

  return new Promise((resolve, reject) => {
    watcher.on(`ready`, () => {
      fsMachine.send({ type: `CHOKIDAR_READY`, resolve, reject })
    })
  })
}

exports.setFieldsOnGraphQLNodeType = require(`./extend-file-node`)
<|endoftext|>