<commit_before>// Create a static 'extends' method on the Object class
// This allows us to extend existing classes
// for classical object-oriented inheritance
Object.extend = function(superClass, definition) {
    var subClass = function() {};
    // Our constructor becomes the 'subclass'
    if (definition.constructor !== Object)
        subClass = definition.constructor;
    subClass.prototype = new superClass();
    for (var prop in definition) {
        if (prop != 'constructor')
            subClass.prototype[prop] = definition[prop];
    }
    return subClass;
};

var delay = (function(){
    var timer = 0;
    return function(callback, ms){
        clearTimeout (timer);
        timer = setTimeout(callback, ms);
    };
})();

var BoltExtender = Object.extend(Object, {

    selector: ".extend-bolt-container",

    constructor: function(){
        jQuery(this.selector).on("change", this, this.events.change);
        jQuery(this.selector).on("click", this, this.events.click);

        jQuery(document).ajaxStart(function() {
            // show loader on start
            active_interval = setInterval(function(){
                active_console.append(".");
            },1000);
        }).ajaxSuccess(function() {
            clearInterval(active_interval);
        });

        this.checkInstalled();
        this.liveSearch();

    },

    find: function(selector) {
        return jQuery(this.selector).find(selector);
    },

    updateCheck: function() {
        var controller = this;

        controller.find('.update-container').show();
        var target = controller.find(".update-output" );
        active_console = target;
        active_console.html("Searching for available updates...");
        jQuery.get(baseurl+'check', function(data) {
            target.html(data);
        });
    },

    updateRun: function() {
        var controller = this;

        controller.find('.update-container').show();
        var target = controller.find(".update-output" );
        active_console = target;
        active_console.html("Running update....");
        jQuery.get(baseurl+'update', function(data) {
            target.html(data);
            setTimeout(function(){
                controller.find('.update-container').hide();
            },7000);
        });
    },

    checkInstalled: function() {
        var controller = this;

        controller.find('.installed-container').each(function(){
            active_console = controller.find(".installed-container .console");
            var target = jQuery(this).find('.installed-list');
            jQuery.get(baseurl+"installed", function(data) {
                target.show();
                if(data.length > 0) {
                    active_console.html(data.length + " installed extension(s).");
                    setTimeout(function(){
                        controller.find(".installed-container .console").hide();
                    },7000);
                    target.find('.installed-list-items').html('');
                    for(var e in data) {
                        ext = data[e];
                        target.find('.installed-list-items').append("
"+ext.title+"	 "+ext.description+"	"+ext.authors+"	 Uninstall
");
                    }
                } else {
                    target.find('.installed-list-items').html("
No Bolt Extensions installed.
");
                    active_console.hide();
                }
            });
        });
    },

    checkPackage: function(e) {
        var controller = this;
        var ext = this.find('input[name="check-package"]').val();
        active_console = this.find(".check-package");
        jQuery.ajax({
            url: site+'list.json',
            dataType: 'jsonp',
            data: {'name': ext},
        })
        .success(function(data) {
            var pack = data.packages[0];
            var tpl = '
';
            tpl+='<input type="hidden" name="package-name" value="'+pack.name+'">'
            tpl+='Select Version To Install
<select name="package-version" class="form-control">';
            for(var v in pack.versions) {
                tpl+='<option value="'+pack.versions[v]+'">'+pack.versions[v]+'</option>'
            }
            tpl +='</select>

 Install Extension

';
            controller.find(".check-package").hide();
            controller.find(".check-package").after(tpl);
        })
        .fail(function() {
            alert('Bolt Extension could not be found. Please check at {{site}} to ensure correct name.')
        });

        e.preventDefault();
    },

    install: function(e) {
        var controller = this;

        var package = controller.find('input[name="package-name"]').val();
        var version = controller.find('select[name="package-version"]').val();

        controller.find('.install-response-container').show();
        active_console = controller.find('.install-response-container .console');
        jQuery.get(
            baseurl+'install',
            {'package':package,'version':version}
        )
        .done(function(data) {
            active_console.html(data);

            setTimeout(function(){
                controller.find('.install-version-container').hide();
                controller.find('.install-response-container').hide();
            }, 2000);
            controller.find(".check-package").show()
            controller.find('input[name="check-package"]').val('');
            controller.checkInstalled();
        });
        e.preventDefault();
    },

    uninstall: function(e) {
        var controller = this;
        var t = this.find('.installed-container .console').html('Preparing to remove package...');
        t.show();
        active_console = t;
        jQuery.get(
            jQuery(e.target).attr("href")
        )
        .done(function(data) {
            controller.find('.installed-container .console').html(data);
            controller.checkInstalled();
            delay(function(){
                active_console.hide();
            }, 2000);
        });

        e.preventDefault();
    },

    liveSearch: function() {
        var livesearch = this.find('input[name="check-package"]');

        livesearch.on('keyup', function(e){
            var searchVal = jQuery(this).val();
            if(searchVal.length < 3) return;
            delay(function(){
                jQuery.ajax({
                    url: site+"list.json",
                    dataType: 'jsonp',
                    data: {'name': searchVal}
                })
                .success(function(data){
                    if(data['packages'].length <1 ) return;
                    var cont = livesearch.parent().find(".auto-search");
                    cont.html("").show();
                    for(var p in data['packages']) {
                        var t = data['packages'][p];
                        cont.append(""+t.name+"");
                    }
                });
            }, 500 );
        });
    },

    prefill: function(e) {
        var target = jQuery(e.target);
        this.find('input[name="check-package"]').val( target.text());
        target.parent().hide();
    },

    events: {
        change: function(e, t){
            var controller = e.data;

        },

        click: function(e, t){
            var controller = e.data;
            switch(jQuery(e.target).data('action')) {
                case "update-check"     : controller.updateCheck(); break;
                case "update-run"       : controller.updateRun(); break;
                case "check-package"    : controller.checkPackage(e.originalEvent); break;
                case "uninstall-package": controller.uninstall(e.originalEvent); break;
                case "install-package"  : controller.install(e.originalEvent); break;
                case "prefill-package"  : controller.prefill(e.originalEvent); break;
            }
        }

    }

});<commit_msg>add wrapper to install label<commit_after>// Create a static 'extends' method on the Object class
// This allows us to extend existing classes
// for classical object-oriented inheritance
Object.extend = function(superClass, definition) {
    var subClass = function() {};
    // Our constructor becomes the 'subclass'
    if (definition.constructor !== Object)
        subClass = definition.constructor;
    subClass.prototype = new superClass();
    for (var prop in definition) {
        if (prop != 'constructor')
            subClass.prototype[prop] = definition[prop];
    }
    return subClass;
};

var delay = (function(){
    var timer = 0;
    return function(callback, ms){
        clearTimeout (timer);
        timer = setTimeout(callback, ms);
    };
})();

var BoltExtender = Object.extend(Object, {

    selector: ".extend-bolt-container",

    constructor: function(){
        jQuery(this.selector).on("change", this, this.events.change);
        jQuery(this.selector).on("click", this, this.events.click);

        jQuery(document).ajaxStart(function() {
            // show loader on start
            active_interval = setInterval(function(){
                active_console.append(".");
            },1000);
        }).ajaxSuccess(function() {
            clearInterval(active_interval);
        });

        this.checkInstalled();
        this.liveSearch();

    },

    find: function(selector) {
        return jQuery(this.selector).find(selector);
    },

    updateCheck: function() {
        var controller = this;

        controller.find('.update-container').show();
        var target = controller.find(".update-output" );
        active_console = target;
        active_console.html("Searching for available updates...");
        jQuery.get(baseurl+'check', function(data) {
            target.html(data);
        });
    },

    updateRun: function() {
        var controller = this;

        controller.find('.update-container').show();
        var target = controller.find(".update-output" );
        active_console = target;
        active_console.html("Running update....");
        jQuery.get(baseurl+'update', function(data) {
            target.html(data);
            setTimeout(function(){
                controller.find('.update-container').hide();
            },7000);
        });
    },

    checkInstalled: function() {
        var controller = this;

        controller.find('.installed-container').each(function(){
            active_console = controller.find(".installed-container .console");
            var target = jQuery(this).find('.installed-list');
            jQuery.get(baseurl+"installed", function(data) {
                target.show();
                if(data.length > 0) {
                    active_console.html(data.length + " installed extension(s).");
                    setTimeout(function(){
                        controller.find(".installed-container .console").hide();
                    },7000);
                    target.find('.installed-list-items').html('');
                    for(var e in data) {
                        ext = data[e];
                        target.find('.installed-list-items').append("
"+ext.title+"	 "+ext.description+"	"+ext.authors+"	 Uninstall
");
                    }
                } else {
                    target.find('.installed-list-items').html("
No Bolt Extensions installed.
");
                    active_console.hide();
                }
            });
        });
    },

    checkPackage: function(e) {
        var controller = this;
        var ext = this.find('input[name="check-package"]').val();
        active_console = this.find(".check-package");
        jQuery.ajax({
            url: site+'list.json',
            dataType: 'jsonp',
            data: {'name': ext},
        })
        .success(function(data) {
            var pack = data.packages[0];
            var tpl = '
';
            tpl+='<input type="hidden" name="package-name" value="'+pack.name+'">'
            tpl+='
Select Version To Install

<select name="package-version" class="form-control">';
            for(var v in pack.versions) {
                tpl+='<option value="'+pack.versions[v]+'">'+pack.versions[v]+'</option>'
            }
            tpl +='</select>

 Install Extension

';
            controller.find(".check-package").hide();
            controller.find(".check-package").after(tpl);
        })
        .fail(function() {
            alert('Bolt Extension could not be found. Please check at {{site}} to ensure correct name.')
        });

        e.preventDefault();
    },

    install: function(e) {
        var controller = this;

        var package = controller.find('input[name="package-name"]').val();
        var version = controller.find('select[name="package-version"]').val();

        controller.find('.install-response-container').show();
        active_console = controller.find('.install-response-container .console');
        jQuery.get(
            baseurl+'install',
            {'package':package,'version':version}
        )
        .done(function(data) {
            active_console.html(data);

            setTimeout(function(){
                controller.find('.install-version-container').hide();
                controller.find('.install-response-container').hide();
            }, 2000);
            controller.find(".check-package").show()
            controller.find('input[name="check-package"]').val('');
            controller.checkInstalled();
        });
        e.preventDefault();
    },

    uninstall: function(e) {
        var controller = this;
        var t = this.find('.installed-container .console').html('Preparing to remove package...');
        t.show();
        active_console = t;
        jQuery.get(
            jQuery(e.target).attr("href")
        )
        .done(function(data) {
            controller.find('.installed-container .console').html(data);
            controller.checkInstalled();
            delay(function(){
                active_console.hide();
            }, 2000);
        });

        e.preventDefault();
    },

    liveSearch: function() {
        var livesearch = this.find('input[name="check-package"]');

        livesearch.on('keyup', function(e){
            var searchVal = jQuery(this).val();
            if(searchVal.length < 3) return;
            delay(function(){
                jQuery.ajax({
                    url: site+"list.json",
                    dataType: 'jsonp',
                    data: {'name': searchVal}
                })
                .success(function(data){
                    if(data['packages'].length <1 ) return;
                    var cont = livesearch.parent().find(".auto-search");
                    cont.html("").show();
                    for(var p in data['packages']) {
                        var t = data['packages'][p];
                        cont.append(""+t.name+"");
                    }
                });
            }, 500 );
        });
    },

    prefill: function(e) {
        var target = jQuery(e.target);
        this.find('input[name="check-package"]').val( target.text());
        target.parent().hide();
    },

    events: {
        change: function(e, t){
            var controller = e.data;

        },

        click: function(e, t){
            var controller = e.data;
            switch(jQuery(e.target).data('action')) {
                case "update-check"     : controller.updateCheck(); break;
                case "update-run"       : controller.updateRun(); break;
                case "check-package"    : controller.checkPackage(e.originalEvent); break;
                case "uninstall-package": controller.uninstall(e.originalEvent); break;
                case "install-package"  : controller.install(e.originalEvent); break;
                case "prefill-package"  : controller.prefill(e.originalEvent); break;
            }
        }

    }

});<|endoftext|>


<commit_before>const sgMail = require('@sendgrid/mail');

sgMail.setApiKey(process.env.SENDGRID_API_KEY);

const snoringCatDomainIndexRouteRegex = /^.*$/;
const snoringCatPathIndexRouteRegex =
    /^\/(snoring-cat|snoringcat|snoring-cat-games|snoringcatgames|snoring-cat-llc|snoring|cat|scg)(?:\/.*)?$/;

const supportRegex = /^.*\/support.*$/;
const logGesturesRegex = /^.*\/log\/gestures.*$/;

const subroutes = [
  {
    // Inner-Tube Climber privacy policy.
    pathRegex: /^.*\/inner-tube-climber\/(privacy-policy|privacy).*$/,
    redirectUrl:
        'https://docs.google.com/document/d/1kH48Xn62wFnZuy8wFmrsr4lKJ-k3wU-MnqFpYdhwBCc/preview',
  },
  {
    // Inner-Tube Climber terms of service.
    pathRegex: /^.*\/inner-tube-climber\/(terms-of-service|terms-and-conditions|tos).*$/,
    redirectUrl:
        'https://docs.google.com/document/d/1g1W4F2nJqJsIPKOwRGlFJi4IGj5q1ae7upYOTnVtfyI/preview',
  },
  {
    // Inner-Tube Climber data-deletion instructions.
    pathRegex: /^.*\/inner-tube-climber\/(data-deletion-instructions|data-deletion).*$/,
    redirectUrl:
        'https://docs.google.com/document/d/1QMl93Ti8aYybPHPmyAFlLnn7U9KyxSzpCI5N50Drqls/preview',
  },
  {
    // Support.
    pathRegex: supportRegex,
    filePath: '/public/support.html',
  },
  {
    // Log gestures.
    pathRegex: logGesturesRegex,
    responseText: 'Gestures successfully recorded',
  },
  {
    // Index.
    pathRegex: /^.*$/,
    filePath: '/public/index.html',
  },
];

const dataDeletionRequestKey = 'request-data-deletion';

// Attaches the route handlers for this app.
exports.attachRoutes = (server, appPath, config) => {
  // Prepend the app path to any sub-route file paths.
  subroutes.forEach(subroute => {
    if (subroute.filePath) {
      subroute.filePath = appPath + subroute.filePath;
    }
  });

  server.get(snoringCatDomainIndexRouteRegex, handleSnoringCatDomainRequest);
  server.get(snoringCatPathIndexRouteRegex, handleSnoringCatPathRequest);

  // ---  --- //

  // Handles a request for this app.
  function handleSnoringCatDomainRequest(req, res, next) {
    // Check whether this request was directed to the portfolio.
    if (config.snoringCatDomains.indexOf(req.hostname) < 0) {
      next();
      return;
    }

    handleSnoringCatPath(req, res, next);
  }

  // Handles a request for this app.
  function handleSnoringCatPathRequest(req, res, next) {
    // Check whether this request was directed to the portfolio.
    if (config.portfolioDomains.indexOf(req.hostname) < 0) {
      next();
    }

    handleSnoringCatPath(req, res, next);
  }

  async function handleSnoringCatPath(req, res, next) {
    if (req.query.hasOwnProperty(dataDeletionRequestKey) && supportRegex.test(req.path)) {
      await sendDataDeletionRequestEmail(req);
    } else if (logGesturesRegex.test(req.path)) {
      await sendLogGesturesEmail(req);
    }

    for (let subroute of subroutes) {
      if (subroute.pathRegex.test(req.path)) {
        if (subroute.filePath) {
          res.sendFile(subroute.filePath);
        } else if (subroute.redirectUrl) {
          res.redirect(subroute.redirectUrl);
        } else if (subroute.responseText) {
          res.send(subroute.responseText);
        } else {
          throw new Error('Invalid route config');
        }
        return;
      }
    }
    throw new Error('Invalid route config');
  }

  async function sendDataDeletionRequestEmail(req) {
    // The user is requesting data deletion.
    // Send an email notification to follow-up.

    const subject =
        'DATA DELETION REQUEST' +
        ': client-id=' + req.query['client-id'] +
        '; source=' + req.query['source'] +
        '; app=' + req.query['app'];

    const body =
        'A user has requested that their data be deleted.' +
        '\n\n-   Now follow-up with Google Analytics to delete all data with the corresponding client ID (' + req.query['client-id'] + ').' +
        '\n-   If the user sends an additional email from the support page, then send them a confirmation email once Google has deleted their data.' +
        '\n\nhttps://support.google.com/analytics/answer/9450800?hl=en';

    const message = {
      from: 'snoringcat@snoringcat.games',
      to: 'support@snoringcat.games',
      subject: subject,
      text: body,
    };

    try {
      await sgMail.send(message);
      console.log('Mail sent');
    } catch (e) {
      console.error('Mail failed to send!', e);
    }
  }

  async function sendLogGesturesEmail(req) {
    // The user has sent gesture information for debugging.
    // Send an email with this information.

    const subject =
        'GESTURE LOGS FOR DEBUGGING' +
        ': source=' + req.query['source'] +
        '; app=' + req.query['app'];

    const body = req.body;

    const message = {
      from: 'snoringcat@snoringcat.games',
      to: 'support@snoringcat.games',
      subject: subject,
      text: body,
    };

    await sgMail.send(message);
    console.log('Mail sent');
  }
};
<commit_msg>Fix for post requests<commit_after>const sgMail = require('@sendgrid/mail');

sgMail.setApiKey(process.env.SENDGRID_API_KEY);

const snoringCatDomainIndexRouteRegex = /^.*$/;
const snoringCatPathIndexRouteRegex =
    /^\/(snoring-cat|snoringcat|snoring-cat-games|snoringcatgames|snoring-cat-llc|snoring|cat|scg)(?:\/.*)?$/;

const supportRegex = /^.*\/support.*$/;
const logGesturesRegex = /^.*\/log\/gestures.*$/;

const subroutes = [
  {
    // Inner-Tube Climber privacy policy.
    pathRegex: /^.*\/inner-tube-climber\/(privacy-policy|privacy).*$/,
    redirectUrl:
        'https://docs.google.com/document/d/1kH48Xn62wFnZuy8wFmrsr4lKJ-k3wU-MnqFpYdhwBCc/preview',
  },
  {
    // Inner-Tube Climber terms of service.
    pathRegex: /^.*\/inner-tube-climber\/(terms-of-service|terms-and-conditions|tos).*$/,
    redirectUrl:
        'https://docs.google.com/document/d/1g1W4F2nJqJsIPKOwRGlFJi4IGj5q1ae7upYOTnVtfyI/preview',
  },
  {
    // Inner-Tube Climber data-deletion instructions.
    pathRegex: /^.*\/inner-tube-climber\/(data-deletion-instructions|data-deletion).*$/,
    redirectUrl:
        'https://docs.google.com/document/d/1QMl93Ti8aYybPHPmyAFlLnn7U9KyxSzpCI5N50Drqls/preview',
  },
  {
    // Support.
    pathRegex: supportRegex,
    filePath: '/public/support.html',
  },
  {
    // Log gestures.
    pathRegex: logGesturesRegex,
    responseText: 'Gestures successfully recorded',
  },
  {
    // Index.
    pathRegex: /^.*$/,
    filePath: '/public/index.html',
  },
];

const dataDeletionRequestKey = 'request-data-deletion';

// Attaches the route handlers for this app.
exports.attachRoutes = (server, appPath, config) => {
  // Prepend the app path to any sub-route file paths.
  subroutes.forEach(subroute => {
    if (subroute.filePath) {
      subroute.filePath = appPath + subroute.filePath;
    }
  });

  server.get(snoringCatDomainIndexRouteRegex, handleSnoringCatDomainRequest);
  server.get(snoringCatPathIndexRouteRegex, handleSnoringCatPathRequest);
  server.post(snoringCatDomainIndexRouteRegex, handleSnoringCatDomainRequest);
  server.post(snoringCatPathIndexRouteRegex, handleSnoringCatPathRequest);

  // ---  --- //

  // Handles a request for this app.
  function handleSnoringCatDomainRequest(req, res, next) {
    // Check whether this request was directed to the portfolio.
    if (config.snoringCatDomains.indexOf(req.hostname) < 0) {
      next();
      return;
    }

    handleSnoringCatPath(req, res, next);
  }

  // Handles a request for this app.
  function handleSnoringCatPathRequest(req, res, next) {
    // Check whether this request was directed to the portfolio.
    if (config.portfolioDomains.indexOf(req.hostname) < 0) {
      next();
    }

    handleSnoringCatPath(req, res, next);
  }

  async function handleSnoringCatPath(req, res, next) {
    if (req.query.hasOwnProperty(dataDeletionRequestKey) && supportRegex.test(req.path)) {
      await sendDataDeletionRequestEmail(req);
    } else if (logGesturesRegex.test(req.path)) {
      await sendLogGesturesEmail(req);
    }

    for (let subroute of subroutes) {
      if (subroute.pathRegex.test(req.path)) {
        if (subroute.filePath) {
          res.sendFile(subroute.filePath);
        } else if (subroute.redirectUrl) {
          res.redirect(subroute.redirectUrl);
        } else if (subroute.responseText) {
          res.send(subroute.responseText);
        } else {
          throw new Error('Invalid route config');
        }
        return;
      }
    }
    throw new Error('Invalid route config');
  }

  async function sendDataDeletionRequestEmail(req) {
    // The user is requesting data deletion.
    // Send an email notification to follow-up.

    const subject =
        'DATA DELETION REQUEST' +
        ': client-id=' + req.query['client-id'] +
        '; source=' + req.query['source'] +
        '; app=' + req.query['app'];

    const body =
        'A user has requested that their data be deleted.' +
        '\n\n-   Now follow-up with Google Analytics to delete all data with the corresponding client ID (' + req.query['client-id'] + ').' +
        '\n-   If the user sends an additional email from the support page, then send them a confirmation email once Google has deleted their data.' +
        '\n\nhttps://support.google.com/analytics/answer/9450800?hl=en';

    const message = {
      from: 'snoringcat@snoringcat.games',
      to: 'support@snoringcat.games',
      subject: subject,
      text: body,
    };

    try {
      await sgMail.send(message);
      console.log('Mail sent');
    } catch (e) {
      console.error('Mail failed to send!', e);
    }
  }

  async function sendLogGesturesEmail(req) {
    // The user has sent gesture information for debugging.
    // Send an email with this information.

    const subject =
        'GESTURE LOGS FOR DEBUGGING' +
        ': source=' + req.query['source'] +
        '; app=' + req.query['app'];

    const body = req.body;

    const message = {
      from: 'snoringcat@snoringcat.games',
      to: 'support@snoringcat.games',
      subject: subject,
      text: body,
    };

    await sgMail.send(message);
    console.log('Mail sent');
  }
};
<|endoftext|>


<commit_before>/* Functions for ProseMirror integration.*/
import {
    whenReady
} from "../common"
import {
    EditorState,
    TextSelection
} from "prosemirror-state"
import {
    EditorView
} from "prosemirror-view"
import {
    history
} from "prosemirror-history"
import {
    baseKeymap
} from "prosemirror-commands"
import {
    keymap
} from "prosemirror-keymap"
import {
    collab
} from "prosemirror-collab"
import {
    tableEditing
} from "prosemirror-tables"
import {
    dropCursor
} from "prosemirror-dropcursor"
import {
    gapCursor
} from "prosemirror-gapcursor"
import {
    buildKeymap
} from "prosemirror-example-setup"

import * as plugins from "../../plugins/editor"
import {
    docSchema
} from "../schema/document"
import {
    ModComments
} from "./comments"
import {
    ModFootnotes
} from "./footnotes"
import {
    ModCitations
} from "./citations"
import {
    ModDB
} from "./databases"
import {
    ModCollab
} from "./collab"
import {
    ModTools
} from "./tools"
import {
    ModTrack,
    acceptAllNoInsertions
} from "./track"
import {
    headerbarModel,
    toolbarModel
} from "./menus"
import {
    ModMarginboxes
} from "./marginboxes"
import {
    ModStyles
} from "./styles"
import {
    ModServerCommunications
} from "./server_communications"
import {
    getSettings
} from "../schema/convert"
import {
    BibliographyDB
} from "../bibliography/database"
import {
    ImageDB
} from "../images/database"

import {
    accessRightsPlugin,
    authorInputPlugin,
    citationRenderPlugin,
    collabCaretsPlugin,
    commentsPlugin,
    footnoteMarkersPlugin,
    headerbarPlugin,
    jumpHiddenNodesPlugin,
    keywordInputPlugin,
    linksPlugin,
    marginboxesPlugin,
    pastePlugin,
    placeholdersPlugin,
    settingsPlugin,
    toolbarPlugin,
    trackPlugin
} from "./state_plugins"
import {
    buildEditorKeymap
} from "./keymap"

export const COMMENT_ONLY_ROLES = ['edit', 'review', 'comment']
export const READ_ONLY_ROLES = ['read', 'read-without-comments']
export const REVIEW_ROLES = ['review']

export class Editor {
    // A class that contains everything that happens on the editor page.
    // It is currently not possible to initialize more than one editor class, as it
    // contains bindings to menu items, etc. that are uniquely defined.
    constructor(id) {
        this.mod = {}
        // Whether the editor is currently waiting for a document update. Set to true
        // initially so that diffs that arrive before document has been loaded are not
        // dealt with.
        this.waitingForDocument = true

        this.docInfo = {
            id,
            rights: '',
            owner: undefined,
            is_owner: false,
            confirmedDoc: false // The latest doc as confirmed by the server.
        }
        this.schema = docSchema
        this.user = false

        this.menu = {
            headerbarModel,
            toolbarModel
        }
        this.client_id = Math.floor(Math.random() * 0xFFFFFFFF)
        this.clientTimeAdjustment = 0
        this.statePlugins = [
            [keymap, () => buildEditorKeymap(this.schema)],
            [keymap, () => buildKeymap(this.schema)],
            [keymap, () => baseKeymap],
            [collab, () => ({clientID: this.client_id})],
            [linksPlugin, () => ({editor: this})],
            [history],
            [dropCursor],
            [gapCursor],
            [tableEditing],
            [jumpHiddenNodesPlugin],
            [placeholdersPlugin, () => ({editor: this})],
            [citationRenderPlugin, () => ({editor: this})],
            [headerbarPlugin, () => ({editor: this})],
            [toolbarPlugin, () => ({editor: this})],
            [collabCaretsPlugin, () => ({editor: this})],
            [footnoteMarkersPlugin, () => ({editor: this})],
            [commentsPlugin, () => ({editor: this})],
            [marginboxesPlugin, () => ({editor: this})],
            [keywordInputPlugin, () => ({editor: this})],
            [authorInputPlugin, () => ({editor: this})],
            [pastePlugin, () => ({editor: this})],
            [accessRightsPlugin, () => ({editor: this})],
            [settingsPlugin, () => ({editor: this})],
            [trackPlugin, () => ({editor: this})]
        ]
        new ModCitations(this)
        new ModFootnotes(this)
        new ModServerCommunications(this)
        new ModDB(this)
    }

    init() {
        whenReady().then(()=>this.initEditor())
    }

    initEditor() {
        // The following two commands prevent Firefox from showing table controls.
        document.execCommand("enableObjectResizing", false, false)
        document.execCommand("enableInlineTableEditing", false, false)
        this.view = new EditorView(document.getElementById('document-editable'), {
            state: EditorState.create({
                schema: this.schema
            }),
            handleDOMEvents: {
                focus: (view, event) => {
                    this.currentView = this.view
                    // We focus once more, as focus may have disappeared due to
                    // disappearing placeholders.
                    view.focus()
                }
            },
            dispatchTransaction: (tr) => {
                let newState = this.view.state.apply(tr)
                this.view.updateState(newState)
                this.mod.collab.docChanges.sendToCollaborators()
            }

        })
        // The editor that is currently being edited in -- main or footnote editor
        this.currentView = this.view
        this.mod.footnotes.init()
        new ModCollab(this)
        new ModTools(this)
        new ModTrack(this)
        new ModMarginboxes(this)
        new ModComments(this)
        new ModStyles(this)
        this.activateFidusPlugins()
        this.mod.serverCommunications.init()
    }

    activateFidusPlugins() {
        // Add plugins.
        this.plugins = {}

        Object.keys(plugins).forEach(plugin => {
            if (typeof plugins[plugin] === 'function') {
                this.plugins[plugin] = new plugins[plugin](this)
                this.plugins[plugin].init()
            }
        })
    }

    receiveDocument(data) {
        // Reset collaboration
        this.mod.collab.docChanges.cancelCurrentlyCheckingVersion()
        this.mod.collab.docChanges.unconfirmedDiffs = {}
        if (this.mod.collab.docChanges.awaitingDiffResponse) {
            this.mod.collab.docChanges.enableDiffSending()
        }
        // Remember location hash to scroll there subsequently.
        let locationHash = window.location.hash

        this.clientTimeAdjustment = Date.now() - data.time

        let doc = data.doc

        this.docInfo = data.doc_info
        this.docInfo.version = doc["v"]

        if (this.docInfo.version === 0) {
            // If the document is new, change the url.
            window.history.replaceState("", "", `/document/${this.docInfo.id}/`)
        }

        if (data.hasOwnProperty('user')) {
            this.user = data.user
        } else {
            this.user = this.docInfo.owner
        }

        this.mod.db.bibDB.setDB(data.doc.bibliography)
        // assign bibDB to be used in document schema.
        this.schema.cached.bibDB = this.mod.db.bibDB
        // assign bibDB to be used in footnote schema.
        this.mod.footnotes.fnEditor.schema.cached.bibDB = this.mod.db.bibDB
        this.mod.db.imageDB.setDB(data.doc.images)
        // assign image DB to be used in document schema.
        this.schema.cached.imageDB = this.mod.db.imageDB
        // assign image DB to be used in footnote schema.
        this.mod.footnotes.fnEditor.schema.cached.imageDB = this.mod.db.imageDB
        this.user.bibDB = new BibliographyDB()
        this.user.bibDB.getDB()
        this.user.imageDB = new ImageDB()
        this.user.imageDB.getDB()

        let stateDoc
        if (doc.contents.type) {
            stateDoc = docSchema.nodeFromJSON({type:'doc', content:[doc.contents]})
        } else {
            stateDoc = this.schema.topNodeType.createAndFill()
        }
        let plugins = this.statePlugins.map(plugin => {
            if (plugin[1]) {
                return plugin[0](plugin[1](doc))
            } else {
                return plugin[0]()
            }
        })

        let stateConfig = {
            schema: this.schema,
            doc: stateDoc,
            plugins
        }

        document.getElementById('flow').classList.remove('hide')
        // Set document in prosemirror
        this.view.updateState(EditorState.create(stateConfig))

        // Set initial confirmed doc
        this.docInfo.confirmedDoc = this.view.state.doc
        this.docInfo.confirmedJson = doc.contents

        // Render footnotes based on main doc
        this.mod.footnotes.fnEditor.renderAllFootnotes()

        //  Setup comment handling
        this.mod.comments.store.reset()
        this.mod.comments.store.loadComments(doc.comments)
        this.mod.marginboxes.view(this.view)
        this.waitingForDocument = false
        if (locationHash.length) {
            this.scrollIdIntoView(locationHash.slice(1))
        }
    }

    // Collect all components of the current doc. Needed for saving and export
    // filters
    getDoc(options={}) {
        let pmArticle = options.changes === 'acceptAllNoInsertions' ?
            acceptAllNoInsertions(this.docInfo.confirmedDoc).firstChild :
            this.docInfo.confirmedDoc.firstChild
        return {
            contents: pmArticle.toJSON(),
            settings: getSettings(pmArticle),
            title: pmArticle.firstChild.textContent.substring(0, 255),
            version: this.docInfo.version,
            comments: this.mod.comments.store.comments,
            id: this.docInfo.id
        }
    }

    // Use PMs scrollIntoView function and adjust for top menu
    scrollIdIntoView(id) {

        let foundPos = false,
            view

        this.view.state.doc.descendants((node, pos) => {
            if (foundPos) {
                return
            } else if ((node.type.name === 'heading' || node.type.name === 'figure') && node.attrs.id === id) {
                foundPos = pos + 1
                view = this.view
            } else {
                let anchorMark = node.marks.find(mark => mark.type.name === 'anchor')
                if (anchorMark && anchorMark.attrs.id === id) {
                    foundPos = pos + 1
                    view = this.view
                }
            }
        })

        if (!foundPos) {
            this.mod.footnotes.fnEditor.view.state.doc.descendants((node, pos) => {
                if (foundPos) {
                    return
                } else if ((node.type.name === 'heading' || node.type.name === 'figure') && node.attrs.id === id) {
                    foundPos = pos + 1
                    view = this.mod.footnotes.fnEditor.view
                } else {
                    let anchorMark = node.marks.find(mark => mark.type.name === 'anchor')
                    if (anchorMark && anchorMark.attrs.id === id) {
                        foundPos = pos + 1
                        view = this.mod.footnotes.fnEditor.view
                    }
                }
            })
        }
        if (foundPos) {
            this.scrollPosIntoView(foundPos, view)
        }

    }

    scrollPosIntoView(pos, view) {
        let topMenuHeight = document.querySelector('header').offsetHeight + 10
        let $pos = view.state.doc.resolve(pos)
        view.dispatch(view.state.tr.setSelection(new TextSelection($pos, $pos)))
        view.focus()
        let distanceFromTop = view.coordsAtPos(pos).top - topMenuHeight
        window.scrollBy(0, distanceFromTop)
        return
    }

}
<commit_msg>improved document server updates<commit_after>/* Functions for ProseMirror integration.*/
import {
    whenReady
} from "../common"
import {
    EditorState,
    TextSelection
} from "prosemirror-state"
import {
    EditorView
} from "prosemirror-view"
import {
    history
} from "prosemirror-history"
import {
    baseKeymap
} from "prosemirror-commands"
import {
    keymap
} from "prosemirror-keymap"
import {
    collab
} from "prosemirror-collab"
import {
    tableEditing
} from "prosemirror-tables"
import {
    dropCursor
} from "prosemirror-dropcursor"
import {
    gapCursor
} from "prosemirror-gapcursor"
import {
    buildKeymap
} from "prosemirror-example-setup"

import * as plugins from "../../plugins/editor"
import {
    docSchema
} from "../schema/document"
import {
    ModComments
} from "./comments"
import {
    ModFootnotes
} from "./footnotes"
import {
    ModCitations
} from "./citations"
import {
    ModDB
} from "./databases"
import {
    ModCollab
} from "./collab"
import {
    ModTools
} from "./tools"
import {
    ModTrack,
    acceptAllNoInsertions
} from "./track"
import {
    headerbarModel,
    toolbarModel
} from "./menus"
import {
    ModMarginboxes
} from "./marginboxes"
import {
    ModStyles
} from "./styles"
import {
    ModServerCommunications
} from "./server_communications"
import {
    getSettings
} from "../schema/convert"
import {
    BibliographyDB
} from "../bibliography/database"
import {
    ImageDB
} from "../images/database"

import {
    accessRightsPlugin,
    authorInputPlugin,
    citationRenderPlugin,
    collabCaretsPlugin,
    commentsPlugin,
    footnoteMarkersPlugin,
    headerbarPlugin,
    jumpHiddenNodesPlugin,
    keywordInputPlugin,
    linksPlugin,
    marginboxesPlugin,
    pastePlugin,
    placeholdersPlugin,
    settingsPlugin,
    toolbarPlugin,
    trackPlugin
} from "./state_plugins"
import {
    buildEditorKeymap
} from "./keymap"

export const COMMENT_ONLY_ROLES = ['edit', 'review', 'comment']
export const READ_ONLY_ROLES = ['read', 'read-without-comments']
export const REVIEW_ROLES = ['review']

export class Editor {
    // A class that contains everything that happens on the editor page.
    // It is currently not possible to initialize more than one editor class, as it
    // contains bindings to menu items, etc. that are uniquely defined.
    constructor(id) {
        this.mod = {}
        // Whether the editor is currently waiting for a document update. Set to true
        // initially so that diffs that arrive before document has been loaded are not
        // dealt with.
        this.waitingForDocument = true

        this.docInfo = {
            id,
            rights: '',
            owner: undefined,
            is_owner: false,
            confirmedDoc: false // The latest doc as confirmed by the server.
        }
        this.schema = docSchema
        this.user = false

        this.menu = {
            headerbarModel,
            toolbarModel
        }
        this.client_id = Math.floor(Math.random() * 0xFFFFFFFF)
        this.clientTimeAdjustment = 0
        this.statePlugins = [
            [keymap, () => buildEditorKeymap(this.schema)],
            [keymap, () => buildKeymap(this.schema)],
            [keymap, () => baseKeymap],
            [collab, () => ({clientID: this.client_id})],
            [linksPlugin, () => ({editor: this})],
            [history],
            [dropCursor],
            [gapCursor],
            [tableEditing],
            [jumpHiddenNodesPlugin],
            [placeholdersPlugin, () => ({editor: this})],
            [citationRenderPlugin, () => ({editor: this})],
            [headerbarPlugin, () => ({editor: this})],
            [toolbarPlugin, () => ({editor: this})],
            [collabCaretsPlugin, () => ({editor: this})],
            [footnoteMarkersPlugin, () => ({editor: this})],
            [commentsPlugin, () => ({editor: this})],
            [marginboxesPlugin, () => ({editor: this})],
            [keywordInputPlugin, () => ({editor: this})],
            [authorInputPlugin, () => ({editor: this})],
            [pastePlugin, () => ({editor: this})],
            [accessRightsPlugin, () => ({editor: this})],
            [settingsPlugin, () => ({editor: this})],
            [trackPlugin, () => ({editor: this})]
        ]
        new ModCitations(this)
        new ModFootnotes(this)
        new ModServerCommunications(this)
        new ModDB(this)
    }

    init() {
        whenReady().then(()=>this.initEditor())
    }

    initEditor() {
        // The following two commands prevent Firefox from showing table controls.
        document.execCommand("enableObjectResizing", false, false)
        document.execCommand("enableInlineTableEditing", false, false)
        this.view = new EditorView(document.getElementById('document-editable'), {
            state: EditorState.create({
                schema: this.schema
            }),
            handleDOMEvents: {
                focus: (view, event) => {
                    this.currentView = this.view
                    // We focus once more, as focus may have disappeared due to
                    // disappearing placeholders.
                    view.focus()
                }
            },
            dispatchTransaction: (tr) => {
                let newState = this.view.state.apply(tr)
                this.view.updateState(newState)
                this.mod.collab.docChanges.sendToCollaborators()
            }

        })
        // The editor that is currently being edited in -- main or footnote editor
        this.currentView = this.view
        this.mod.footnotes.init()
        new ModCollab(this)
        new ModTools(this)
        new ModTrack(this)
        new ModMarginboxes(this)
        new ModComments(this)
        new ModStyles(this)
        this.activateFidusPlugins()
        this.mod.serverCommunications.init()
    }

    activateFidusPlugins() {
        // Add plugins.
        this.plugins = {}

        Object.keys(plugins).forEach(plugin => {
            if (typeof plugins[plugin] === 'function') {
                this.plugins[plugin] = new plugins[plugin](this)
                this.plugins[plugin].init()
            }
        })
    }

    receiveDocument(data) {
        // Reset collaboration
        this.mod.collab.docChanges.cancelCurrentlyCheckingVersion()
        this.mod.collab.docChanges.unconfirmedDiffs = {}
        if (this.mod.collab.docChanges.awaitingDiffResponse) {
            this.mod.collab.docChanges.enableDiffSending()
        }
        // Remember location hash to scroll there subsequently.
        let locationHash = window.location.hash

        this.clientTimeAdjustment = Date.now() - data.time

        let doc = data.doc

        this.docInfo = data.doc_info
        this.docInfo.version = doc["v"]

        if (this.docInfo.version === 0) {
            // If the document is new, change the url.
            window.history.replaceState("", "", `/document/${this.docInfo.id}/`)
        }

        if (data.hasOwnProperty('user')) {
            this.user = data.user
        } else {
            this.user = this.docInfo.owner
        }

        this.mod.db.bibDB.setDB(data.doc.bibliography)
        // assign bibDB to be used in document schema.
        this.schema.cached.bibDB = this.mod.db.bibDB
        // assign bibDB to be used in footnote schema.
        this.mod.footnotes.fnEditor.schema.cached.bibDB = this.mod.db.bibDB
        this.mod.db.imageDB.setDB(data.doc.images)
        // assign image DB to be used in document schema.
        this.schema.cached.imageDB = this.mod.db.imageDB
        // assign image DB to be used in footnote schema.
        this.mod.footnotes.fnEditor.schema.cached.imageDB = this.mod.db.imageDB
        this.user.bibDB = new BibliographyDB()
        this.user.bibDB.getDB()
        this.user.imageDB = new ImageDB()
        this.user.imageDB.getDB()

        let stateDoc
        if (doc.contents.type) {
            stateDoc = docSchema.nodeFromJSON({type:'doc', content:[doc.contents]})
        } else {
            stateDoc = this.schema.topNodeType.createAndFill()
        }
        let plugins = this.statePlugins.map(plugin => {
            if (plugin[1]) {
                return plugin[0](plugin[1](doc))
            } else {
                return plugin[0]()
            }
        })

        let stateConfig = {
            schema: this.schema,
            doc: stateDoc,
            plugins
        }

        document.getElementById('flow').classList.remove('hide')
        // Set document in prosemirror
        this.view.updateState(EditorState.create(stateConfig))

        // Set initial confirmed doc
        this.docInfo.confirmedDoc = this.view.state.doc
        this.docInfo.confirmedJson = JSON.parse(JSON.stringify(doc.contents))

        // Render footnotes based on main doc
        this.mod.footnotes.fnEditor.renderAllFootnotes()

        //  Setup comment handling
        this.mod.comments.store.reset()
        this.mod.comments.store.loadComments(doc.comments)
        this.mod.marginboxes.view(this.view)
        this.waitingForDocument = false
        if (locationHash.length) {
            this.scrollIdIntoView(locationHash.slice(1))
        }
    }

    // Collect all components of the current doc. Needed for saving and export
    // filters
    getDoc(options={}) {
        let pmArticle = options.changes === 'acceptAllNoInsertions' ?
            acceptAllNoInsertions(this.docInfo.confirmedDoc).firstChild :
            this.docInfo.confirmedDoc.firstChild
        return {
            contents: pmArticle.toJSON(),
            settings: getSettings(pmArticle),
            title: pmArticle.firstChild.textContent.substring(0, 255),
            version: this.docInfo.version,
            comments: this.mod.comments.store.comments,
            id: this.docInfo.id
        }
    }

    // Use PMs scrollIntoView function and adjust for top menu
    scrollIdIntoView(id) {

        let foundPos = false,
            view

        this.view.state.doc.descendants((node, pos) => {
            if (foundPos) {
                return
            } else if ((node.type.name === 'heading' || node.type.name === 'figure') && node.attrs.id === id) {
                foundPos = pos + 1
                view = this.view
            } else {
                let anchorMark = node.marks.find(mark => mark.type.name === 'anchor')
                if (anchorMark && anchorMark.attrs.id === id) {
                    foundPos = pos + 1
                    view = this.view
                }
            }
        })

        if (!foundPos) {
            this.mod.footnotes.fnEditor.view.state.doc.descendants((node, pos) => {
                if (foundPos) {
                    return
                } else if ((node.type.name === 'heading' || node.type.name === 'figure') && node.attrs.id === id) {
                    foundPos = pos + 1
                    view = this.mod.footnotes.fnEditor.view
                } else {
                    let anchorMark = node.marks.find(mark => mark.type.name === 'anchor')
                    if (anchorMark && anchorMark.attrs.id === id) {
                        foundPos = pos + 1
                        view = this.mod.footnotes.fnEditor.view
                    }
                }
            })
        }
        if (foundPos) {
            this.scrollPosIntoView(foundPos, view)
        }

    }

    scrollPosIntoView(pos, view) {
        let topMenuHeight = document.querySelector('header').offsetHeight + 10
        let $pos = view.state.doc.resolve(pos)
        view.dispatch(view.state.tr.setSelection(new TextSelection($pos, $pos)))
        view.focus()
        let distanceFromTop = view.coordsAtPos(pos).top - topMenuHeight
        window.scrollBy(0, distanceFromTop)
        return
    }

}
<|endoftext|>


<commit_before>define([
    'uijet_dir/uijet',
    'resources',
    'controllers/SearchedList'
], function (uijet, resources) {

    uijet.declare([{
        type    : 'Pane',
        config  : {
            element     : '#nodes_list_container',
            dont_wake   : true,
            app_events  : {
                'entities_list.selected': function ($selected) {
                    this.wake({ entity_id : $selected.attr('data-id') });
                }
            }
        }
    }, {
        type    : 'List',
        config  : {
            element     : '#nodes_list_header',
            horizontal  : true,
            position    : 'top:2rem fluid',
            signals     : {
                pre_select  : function ($selected) {
                    if ( this.$selected && $selected[0] === this.$selected[0] ) {
                        this.$selected.toggleClass('desc');
                    }
                    return {
                        column  : $selected.attr('data-column'),
                        desc    : $selected.hasClass('desc')
                    };
                }
            }
        }
    }, {
        type    : 'List',
        config  : {
            element     : '#nodes_list',
            mixins      : ['Templated', 'Scrolled'],
            adapters    : ['jqWheelScroll', 'Spin', 'SearchedList'],
            resource    : 'LatestTemplate',
            position    : 'fluid',
            search      : {
                fields  : {
                    name        : 10,
                    description : 1,
                    code        : 20
                }
            },
            sorting     : {
                name        : 'name',
                '-name'     : resources.utils.reverseSorting('name'),
                code        : 'code',
                '-code'     : resources.utils.reverseSorting('code'),
                direction   : 'direction',
                '-direction': resources.utils.reverseSorting('direction')
            },
            signals     : {
                post_init       : function () {
                    this.scope = null;
                },
                pre_wake        : function () {
                    var entity_id = this.context.entity_id;
                    if ( entity_id ) {
                        if ( this.latest_entity_id !== entity_id ) {
                            this.latest_entity_id = entity_id;
                            // this makes sure search index is rebuilt and view is re-rendered
                            this.changed = true;
                            // this makes sure the resource will execute fetch to sync with remote server
                            this.has_data = false;
                            this.scope = null;
                            this.resource.url = API_URL + 'nodes/latest/' + entity_id + '/';
                            this.filter(this.resource.roots);
                        }
                        else {
                            this.changed = false;
                        }
                    }
                    return this.changed;
                },
                pre_update      : 'spin',
                post_fetch_data : 'spinOff',
                post_render     : function () {
                    this.$children = this.$element.children();
                    this.publish('rendered');
                },
                post_wake       : function () {
                    if ( this.changed ) {
                        this.index()
                            .search_index.add( this.resource.byAncestor(this.scope) );
                        this.publish('ready', this.context);
                    }
                },
                pre_select      : function ($selected) {
                    return ! $selected[0].hasAttribute('data-leaf') && +$selected.attr('data-id');
                },
                post_select     : function ($selected) {
                    var node_id = +$selected.attr('data-id') || null;
                    // make sure we rebuild index and re-render
                    this.changed = true;
                    this.scope = node_id || null;
                    this.filter(this.resource.byParent, node_id)
                        .wake(true);
                }
            },
            app_events  : {
                'nodes_search.changed'                      : 'filterItems+',
                'nodes_search.exited'                       : function (query) {
                    if ( ! query ) {
                        this.changed = true;
                        this.filter(this.resource.byParent, this.scope)
                            .wake(true);
                    }
                },
                'nodes_list.filtered'                       : 'scroll',
                'filters_search.clicked'                    : function () {
                    this.changed = true;
                    this.filter(this.resource.byAncestor, this.scope)
                        .wake(true);
                },
                'node_breadcrumb_main.clicked'              : function () {
                    this.changed = true;
                    this.scope = null;
                    this.wake('roots');
                },
                'node_breadcrumb_back.clicked'              : function (data) {
                    var scope = data.context.id;
                    this.changed = true;
                    this.scope = scope;
                    this.filter(this.resource.byParent, scope)
                        .wake(true);
                },
                'nodes_breadcrumbs.selected'                : 'post_select+',
                'nodes_breadcrumbs_history_menu.selected'   : 'post_select+',
                'nodes_list_header.selected'                : function (data) {
                    this.sort((data.desc ? '-' : '') + data.column);
                    this.changed = true;
                    this.wake(true);
                }
            }
        }
    }]);

});
<commit_msg>Some cleanup<commit_after>define([
    'uijet_dir/uijet',
    'resources',
    'controllers/SearchedList'
], function (uijet, resources) {

    uijet.declare([{
        type    : 'Pane',
        config  : {
            element     : '#nodes_list_container',
            dont_wake   : true,
            app_events  : {
                'entities_list.selected': function ($selected) {
                    this.wake({ entity_id : $selected.attr('data-id') });
                }
            }
        }
    }, {
        type    : 'List',
        config  : {
            element     : '#nodes_list_header',
            horizontal  : true,
            position    : 'top:2rem fluid',
            signals     : {
                pre_select  : function ($selected) {
                    if ( this.$selected && $selected[0] === this.$selected[0] ) {
                        this.$selected.toggleClass('desc');
                    }
                    return {
                        column  : $selected.attr('data-column'),
                        desc    : $selected.hasClass('desc')
                    };
                }
            }
        }
    }, {
        type    : 'List',
        config  : {
            element     : '#nodes_list',
            mixins      : ['Templated', 'Scrolled'],
            adapters    : ['jqWheelScroll', 'Spin', 'SearchedList'],
            resource    : 'LatestTemplate',
            position    : 'fluid',
            search      : {
                fields  : {
                    name        : 10,
                    description : 1,
                    code        : 20
                }
            },
            sorting     : {
                name        : 'name',
                '-name'     : resources.utils.reverseSorting('name'),
                code        : 'code',
                '-code'     : resources.utils.reverseSorting('code'),
                direction   : 'direction',
                '-direction': resources.utils.reverseSorting('direction')
            },
            signals     : {
                post_init       : function () {
                    this.scope = null;
                },
                pre_wake        : function () {
                    var entity_id = this.context.entity_id;
                    if ( entity_id ) {
                        if ( this.latest_entity_id !== entity_id ) {
                            this.latest_entity_id = entity_id;
                            // this makes sure search index is rebuilt and view is re-rendered
                            this.changed = true;
                            // this makes sure the resource will execute fetch to sync with remote server
                            this.has_data = false;
                            this.scope = null;
                            this.resource.url = API_URL + 'nodes/latest/' + entity_id + '/';
                            this.filter(this.resource.roots);
                        }
                        else {
                            this.changed = false;
                        }
                    }
                    return this.changed;
                },
                pre_update      : 'spin',
                post_fetch_data : 'spinOff',
                post_render     : function () {
                    this.$children = this.$element.children();
                    this.publish('rendered');
                },
                post_wake       : function () {
                    if ( this.changed ) {
                        this.index()
                            .search_index.add( this.resource.byAncestor(this.scope) );
                        this.publish('ready', this.context);
                    }
                },
                pre_select      : function ($selected) {
                    return ! $selected[0].hasAttribute('data-leaf') && +$selected.attr('data-id');
                },
                post_select     : function ($selected) {
                    var node_id = +$selected.attr('data-id') || null;
                    // make sure we rebuild index and re-render
                    this.changed = true;
                    this.scope = node_id || null;
                    this.filter(this.resource.byParent, node_id)
                        .wake(true);
                }
            },
            app_events  : {
                'nodes_search.changed'                      : 'filterItems+',
                'nodes_search.exited'                       : function (query) {
                    if ( ! query ) {
                        this.changed = true;
                        this.filter(this.resource.byParent, this.scope)
                            .wake(true);
                    }
                },
                'nodes_list.filtered'                       : 'scroll',
                'filters_search.clicked'                    : function () {
                    this.changed = true;
                    this.filter(this.resource.byAncestor, this.scope)
                        .wake(true);
                },
                'node_breadcrumb_main.clicked'              : function () {
                    this.changed = true;
                    this.scope = null;
                    this.wake('roots');
                },
                'node_breadcrumb_back.clicked'              : function (data) {
                    var scope = data.context.id;
                    this.changed = true;
                    this.scope = scope;
                    this.filter(this.resource.byParent, scope)
                        .wake(true);
                },
                'nodes_breadcrumbs.selected'                : 'post_select+',
                'nodes_breadcrumbs_history_menu.selected'   : 'post_select+',
                'nodes_list_header.selected'                : function (data) {
                    this.changed = true;
                    this.sort((data.desc ? '-' : '') + data.column)
                        .wake(true);
                }
            }
        }
    }]);

});
<|endoftext|>


<commit_before>defineAttr(OpenClassOrInterfaceType$meta$declaration.$$.prototype,'typeArguments',function(){
  var tps=this.declaration.tipo.$$metamodel$$.$tp;
  if (tps) {
    var rtps = this.declaration._targs;
    var targs=[];
    for (var tpn in tps) {
      var rtp=rtps&&rtps[tpn];
      var otp=OpenTypeParam(this.declaration.tipo,tpn);
      var targ;
      if (rtp===undefined) {
        targ = new OpenTvar(otp);
      } else {
        targ = _openTypeFromTarg(rtp);
      }
      targs.push(Entry(otp, targ, {Key:{t:TypeParameter$meta$declaration},Item:{t:OpenType$meta$declaration}}));
    }
    return LazyMap(targs.reifyCeylonType({Absent:{t:Null},Element:{t:Entry,a:{Key:{t:TypeParameter$meta$declaration},Item:{t:OpenType$meta$declaration}}}}),{Key:{t:TypeParameter$meta$declaration},Item:{t:OpenType$meta$declaration}});
  }
  return getEmpty();
},undefined,function(){return{mod:$$METAMODEL$$,$t:{t:Map,a:{Key:{t:TypeParameter$meta$declaration},Item:{t:OpenType$meta$declaration}}},$cont:OpenClassOrInterfaceType,$an:function(){return[shared(),actual()];},d:['ceylon.language.meta.declaration','OpenClassOrInterfaceType','$at','typeArguments']};});
defineAttr(OpenClassOrInterfaceType$meta$declaration.$$.prototype,'extendedType',function(){
  return this.declaration.extendedType;
},undefined,function(){return{mod:$$METAMODEL$$,$t:{ t:'u', l:[{t:Null},{t:OpenClassType$meta$declaration}]},$cont:OpenClassOrInterfaceType,$an:function(){return[shared(),actual()];},d:['ceylon.language.meta.declaration','OpenClassOrInterfaceType','$at','extendedType']};});
defineAttr(OpenClassOrInterfaceType$meta$declaration.$$.prototype,'declaration',function(){return this._decl;},undefined,function(){return{mod:$$METAMODEL$$,$t:{t:ClassDeclaration$meta$declaration},$cont:OpenClassOrInterfaceType,$an:function(){return[shared(),actual()];},d:['ceylon.language.meta.declaration','OpenClassOrInterfaceType','$at','declaration']};});
defineAttr(OpenClassOrInterfaceType$meta$declaration.$$.prototype,'satisfiedTypes',function(){
  return this.declaration.satisfiedTypes;
},undefined,function(){return{mod:$$METAMODEL$$,$t:{t:Sequential,a:{Element:{t:OpenInterfaceType$meta$declaration}}},$cont:OpenClassOrInterfaceType$meta$declaration,$an:function(){return[shared(),actual()];},d:['ceylon.language.meta.declaration','OpenClassOrInterfaceType','$at','satisfiedTypes']};});
defineAttr(OpenClassOrInterfaceType$meta$declaration.$$.prototype,'string',function(){
  var s=this.declaration.string;
  var tps=this.declaration.tipo.$$metamodel$$.$tp;
  if (tps) {
    var rtps=this.declaration._targs;
    s+="<";
    var first=true;
    for (var t in tps) {
      var rtp=rtps&&rtps[t];
      if (first)first=false;else s+=",";
      if (rtp===undefined) {
        s+=t;
      } else {
        s+=_openTypeFromTarg(rtp).string;
      }
    }
    s+=">";
  }
  return s;
},undefined,function(){return{mod:$$METAMODEL$$,$t:{t:String$},d:['ceylon.language','Object','$at','string']};});
<commit_msg>fix OpenClassOrInterface.typeArguments<commit_after>defineAttr(OpenClassOrInterfaceType$meta$declaration.$$.prototype,'typeArguments',function(){
  var tps=this.declaration.tipo.$$metamodel$$.$tp;
  if (tps) {
    var rtps = this.declaration._targs;
    var targs=[];
    for (var tpn in tps) {
      var rtp=rtps&&rtps[tpn];
      var otp=OpenTypeParam(this.declaration.tipo,tpn);
      var targ;
      if (rtp===undefined) {
        targ = OpenTvar(otp);
      } else if (typeof(rtp)==='string') {
        targ = OpenTvar(OpenTypeParam(this.declaration.tipo,rtp));
      } else {
        targ = _openTypeFromTarg(rtp);
      }
      targs.push(Entry(otp, targ, {Key:{t:TypeParameter$meta$declaration},Item:{t:OpenType$meta$declaration}}));
    }
    return LazyMap(targs.reifyCeylonType({Absent:{t:Null},Element:{t:Entry,a:{Key:{t:TypeParameter$meta$declaration},Item:{t:OpenType$meta$declaration}}}}),{Key:{t:TypeParameter$meta$declaration},Item:{t:OpenType$meta$declaration}});
  }
  return getEmpty();
},undefined,function(){return{mod:$$METAMODEL$$,$t:{t:Map,a:{Key:{t:TypeParameter$meta$declaration},Item:{t:OpenType$meta$declaration}}},$cont:OpenClassOrInterfaceType,$an:function(){return[shared(),actual()];},d:['ceylon.language.meta.declaration','OpenClassOrInterfaceType','$at','typeArguments']};});
defineAttr(OpenClassOrInterfaceType$meta$declaration.$$.prototype,'extendedType',function(){
  return this.declaration.extendedType;
},undefined,function(){return{mod:$$METAMODEL$$,$t:{ t:'u', l:[{t:Null},{t:OpenClassType$meta$declaration}]},$cont:OpenClassOrInterfaceType,$an:function(){return[shared(),actual()];},d:['ceylon.language.meta.declaration','OpenClassOrInterfaceType','$at','extendedType']};});
defineAttr(OpenClassOrInterfaceType$meta$declaration.$$.prototype,'declaration',function(){return this._decl;},undefined,function(){return{mod:$$METAMODEL$$,$t:{t:ClassDeclaration$meta$declaration},$cont:OpenClassOrInterfaceType,$an:function(){return[shared(),actual()];},d:['ceylon.language.meta.declaration','OpenClassOrInterfaceType','$at','declaration']};});
defineAttr(OpenClassOrInterfaceType$meta$declaration.$$.prototype,'satisfiedTypes',function(){
  return this.declaration.satisfiedTypes;
},undefined,function(){return{mod:$$METAMODEL$$,$t:{t:Sequential,a:{Element:{t:OpenInterfaceType$meta$declaration}}},$cont:OpenClassOrInterfaceType$meta$declaration,$an:function(){return[shared(),actual()];},d:['ceylon.language.meta.declaration','OpenClassOrInterfaceType','$at','satisfiedTypes']};});
defineAttr(OpenClassOrInterfaceType$meta$declaration.$$.prototype,'string',function(){
  var s=this.declaration.string;
  var tps=this.declaration.tipo.$$metamodel$$.$tp;
  if (tps) {
    var rtps=this.declaration._targs;
    s+="<";
    var first=true;
    for (var t in tps) {
      var rtp=rtps&&rtps[t];
      if (first)first=false;else s+=",";
      if (rtp===undefined) {
        s+=t;
      } else {
        s+=_openTypeFromTarg(rtp).string;
      }
    }
    s+=">";
  }
  return s;
},undefined,function(){return{mod:$$METAMODEL$$,$t:{t:String$},d:['ceylon.language','Object','$at','string']};});
<|endoftext|>


<commit_before>const experiments = require('../../../db/model/experiments');
const processes = require('../../../db/model/processes');
const check = require('../../../db/model/check');
const schema = require('../../../schema');
const parse = require('co-body');
const status = require('http-status');
const _ = require('lodash');
const propertyValidator = require('../../../schema/property-validator');
const validators = require('./validators');
const ra = require('../../resource-access');
const Router = require('koa-router');

function *getProcessesForExperiment(next) {
    let rv = yield experiments.getProcessesForExperiment(this.params.experiment_id);
    if (rv.error) {
        this.status = status.BAD_REQUEST;
        this.body = rv;
    } else {
        this.body = rv.val;
    }
    yield next;
}

function* createProcessInExperimentFromTemplate(next) {
    let rv = yield experiments.addProcessFromTemplate(this.params.project_id, this.params.experiment_id,
        this.params.template_id, this.reqctx.user.id);
    if (rv.error) {
        this.status = status.BAD_REQUEST;
        this.body = rv;
    } else {
        this.body = rv.val;
    }
    yield next;
}

function* updateExperimentProcess(next) {
    let updateArgs = yield parse(this);
    updateArgs.process_id = this.params.process_id;
    let errors = yield validateUpdateExperimentProcessTemplateArgs(updateArgs, this.params);
    if (errors != null) {
        this.status = status.BAD_REQUEST;
        this.body = errors;
    } else {
        let rv = yield experiments.updateProcess(this.params.experiment_id, this.params.process_id,
            updateArgs.properties, updateArgs.files, updateArgs.samples);
        if (rv.error) {
            this.status = status.BAD_REQUEST;
            this.body = rv;
        } else {
            this.body = rv.val;
        }
    }

    yield next;
}

function* validateUpdateExperimentProcessTemplateArgs(updateArgs, params) {
    console.log('updateArgs', updateArgs);
    if (updateArgs.properties && !_.isArray(updateArgs.properties)) {
        return {error: `Properties attribute isn't an array`};
    }

    if (updateArgs.process_id) {
        let isTemplateForProcess = yield check.isTemplateForProcess(updateArgs.template_id, updateArgs.process_id);
        if (!isTemplateForProcess) {
            return {error: `Incorrect template for process; template: ${updateArgs.template_id} process: ${params.process_id}`};
        }
    }

    let template = yield experiments.getTemplate(updateArgs.template_id);

    if (updateArgs.properties) {
        for (let i = 0; i < updateArgs.properties.length; i++) {
            console.log('updateArgs', i, updateArgs.properties[i]);
            let property = updateArgs.properties[i];
            console.log('calling validateProperty with', property);
            let errors = yield validateProperty(template, property);
            if (errors !== null) {
                return errors;
            }
        }
    }

    if (updateArgs.files && !_.isArray(updateArgs.files)) {
        return {error: `Files attribute isn't an array`};
    }

    if (updateArgs.files && !updateArgs.process_id) {
        return {error: `Must supply a process when including files`};
    }

    if (updateArgs.files) {
        for (let i = 0; i < updateArgs.files.length; i++) {
            let f = updateArgs.files[i];
            let errors = yield validateFile(params.project_id, f);
            if (errors !== null) {
                return errors;
            }
        }
    }

    if (updateArgs.samples && !_.isArray(updateArgs.samples)) {
        return {error: `Samples attribute isn't an array`};
    }

    if (updateArgs.samples && !updateArgs.process_id) {
        return {error: `Must supply a process when including samples`};
    }

    if (updateArgs.samples) {
        for (let i = 0; i < updateArgs.samples.length; i++) {
            let s = updateArgs.samples[i];
            let errors = yield validators.validateSample(params.project_id, s);
            if (errors !== null) {
                return errors;
            }
        }
    }

    return null;
}

function* validateProperty(template, property) {
    console.log('validateProperty', property);
    let errors = yield schema.validate(schema.templateProperty, property);
    if (errors !== null) {
        return errors;
    }

    console.log('after schema validate', property);

    if (!propertyValidator.isValidSetupProperty(template, property)) {
        console.log('isValidSetupProperty', property);
        return {error: `Invalid property ${property.attribute}`};
    }

    return null;
    // return {error: `something bad happened`};
}

function* validateFile(projectId, file) {
    let errors = yield schema.validate(schema.templateCommand, file);
    if (errors !== null) {
        return errors;
    }

    if (file.command !== 'add' && file.command !== 'delete') {
        return {error: `Bad command '${file.command} for file ${file.id}`};
    }

    let fileInProject = yield check.fileInProject(file.id, projectId);
    if (!fileInProject) {
        return {error: `File ${file.id} not in project ${projectId}`};
    }

    return null;
}

function* getProcess(next) {
    let rv = yield processes.getProcess(this.params.process_id);
    if (rv.error) {
        this.status = status.BAD_REQUEST;
        this.body = rv;
    } else {
        this.body = rv.val;
    }
    yield next;
}

function createResource() {
    const router = new Router();

    router.get('/', getProcessesForExperiment);
    router.post('/templates/:template_id', ra.validateTemplateExists, createProcessInExperimentFromTemplate);

    router.use('/:process_id', ra.validateProcessInExperiment);

    router.put('/:process_id', updateExperimentProcess);
    router.get('/:process_id', getProcess);

    return router;
}

module.exports = {
    createResource
};
<commit_msg>Remove console statements.<commit_after>const experiments = require('../../../db/model/experiments');
const processes = require('../../../db/model/processes');
const check = require('../../../db/model/check');
const schema = require('../../../schema');
const parse = require('co-body');
const status = require('http-status');
const _ = require('lodash');
const propertyValidator = require('../../../schema/property-validator');
const validators = require('./validators');
const ra = require('../../resource-access');
const Router = require('koa-router');

function *getProcessesForExperiment(next) {
    let rv = yield experiments.getProcessesForExperiment(this.params.experiment_id);
    if (rv.error) {
        this.status = status.BAD_REQUEST;
        this.body = rv;
    } else {
        this.body = rv.val;
    }
    yield next;
}

function* createProcessInExperimentFromTemplate(next) {
    let rv = yield experiments.addProcessFromTemplate(this.params.project_id, this.params.experiment_id,
        this.params.template_id, this.reqctx.user.id);
    if (rv.error) {
        this.status = status.BAD_REQUEST;
        this.body = rv;
    } else {
        this.body = rv.val;
    }
    yield next;
}

function* updateExperimentProcess(next) {
    let updateArgs = yield parse(this);
    updateArgs.process_id = this.params.process_id;
    let errors = yield validateUpdateExperimentProcessTemplateArgs(updateArgs, this.params);
    if (errors != null) {
        this.status = status.BAD_REQUEST;
        this.body = errors;
    } else {
        let rv = yield experiments.updateProcess(this.params.experiment_id, this.params.process_id,
            updateArgs.properties, updateArgs.files, updateArgs.samples);
        if (rv.error) {
            this.status = status.BAD_REQUEST;
            this.body = rv;
        } else {
            this.body = rv.val;
        }
    }

    yield next;
}

function* validateUpdateExperimentProcessTemplateArgs(updateArgs, params) {
    if (updateArgs.properties && !_.isArray(updateArgs.properties)) {
        return {error: `Properties attribute isn't an array`};
    }

    if (updateArgs.process_id) {
        let isTemplateForProcess = yield check.isTemplateForProcess(updateArgs.template_id, updateArgs.process_id);
        if (!isTemplateForProcess) {
            return {error: `Incorrect template for process; template: ${updateArgs.template_id} process: ${params.process_id}`};
        }
    }

    let template = yield experiments.getTemplate(updateArgs.template_id);

    if (updateArgs.properties) {
        for (let i = 0; i < updateArgs.properties.length; i++) {
            let property = updateArgs.properties[i];
            let errors = yield validateProperty(template, property);
            if (errors !== null) {
                return errors;
            }
        }
    }

    if (updateArgs.files && !_.isArray(updateArgs.files)) {
        return {error: `Files attribute isn't an array`};
    }

    if (updateArgs.files && !updateArgs.process_id) {
        return {error: `Must supply a process when including files`};
    }

    if (updateArgs.files) {
        for (let i = 0; i < updateArgs.files.length; i++) {
            let f = updateArgs.files[i];
            let errors = yield validateFile(params.project_id, f);
            if (errors !== null) {
                return errors;
            }
        }
    }

    if (updateArgs.samples && !_.isArray(updateArgs.samples)) {
        return {error: `Samples attribute isn't an array`};
    }

    if (updateArgs.samples && !updateArgs.process_id) {
        return {error: `Must supply a process when including samples`};
    }

    if (updateArgs.samples) {
        for (let i = 0; i < updateArgs.samples.length; i++) {
            let s = updateArgs.samples[i];
            let errors = yield validators.validateSample(params.project_id, s);
            if (errors !== null) {
                return errors;
            }
        }
    }

    return null;
}

function* validateProperty(template, property) {
    let errors = yield schema.validate(schema.templateProperty, property);
    if (errors !== null) {
        return errors;
    }

    if (!propertyValidator.isValidSetupProperty(template, property)) {
        return {error: `Invalid property ${property.attribute}`};
    }

    return null;
    // return {error: `something bad happened`};
}

function* validateFile(projectId, file) {
    let errors = yield schema.validate(schema.templateCommand, file);
    if (errors !== null) {
        return errors;
    }

    if (file.command !== 'add' && file.command !== 'delete') {
        return {error: `Bad command '${file.command} for file ${file.id}`};
    }

    let fileInProject = yield check.fileInProject(file.id, projectId);
    if (!fileInProject) {
        return {error: `File ${file.id} not in project ${projectId}`};
    }

    return null;
}

function* getProcess(next) {
    let rv = yield processes.getProcess(this.params.process_id);
    if (rv.error) {
        this.status = status.BAD_REQUEST;
        this.body = rv;
    } else {
        this.body = rv.val;
    }
    yield next;
}

function createResource() {
    const router = new Router();

    router.get('/', getProcessesForExperiment);
    router.post('/templates/:template_id', ra.validateTemplateExists, createProcessInExperimentFromTemplate);

    router.use('/:process_id', ra.validateProcessInExperiment);

    router.put('/:process_id', updateExperimentProcess);
    router.get('/:process_id', getProcess);

    return router;
}

module.exports = {
    createResource
};
<|endoftext|>


<commit_before>import './App.css';
import classNames from 'classnames';
import clientHealthCheck from './client_health_check';
import CloseIcon from '@material-ui/icons/Close';
import Content from '../content/Content';
import CssBaseline from '@material-ui/core/CssBaseline';
import Dashboard from '../dashboard/Dashboard';
import { defineMessages, injectIntl } from '../../shared/i18n';
import ErrorBoundary from '../error/ErrorBoundary';
import Footer from './Footer';
import Header from './Header';
import IconButton from '@material-ui/core/IconButton';
import { Route, Switch, withRouter } from 'react-router-dom';
import React, { Component } from 'react';
import Search from '../content/Search';
import { SnackbarProvider } from 'notistack';
import styles from './constants.module.css';
import UserContext from './User_Context';

const messages = defineMessages({
  close: { msg: 'Close' },
});

// This is the main entry point on the client-side.
@injectIntl
class App extends Component {
  constructor(props) {
    super(props);

    this.state = {
      userContext: {
        user: props.user,
      },
      devOnlyHiddenOnLoad: process.env.NODE_ENV === 'development',
    };
  }

  componentDidMount() {
    // Remove MaterialUI's SSR generated CSS.
    const jssStyles = document.getElementById('jss-ssr');
    if (jssStyles && jssStyles.parentNode) {
      jssStyles.parentNode.removeChild(jssStyles);
    }

    // Upon starting the app, kick off a client health check which runs periodically.
    clientHealthCheck();

    this.setState({ devOnlyHiddenOnLoad: false });
  }

  renderDashboard() {
    return <Dashboard />;
    // TODO(mime): Can't get Suspense/lazy to play nicely with CSS modules yet.
    // TODO(mime): Suspense and lazy aren't supported by ReactDOMServer yet (breaks SSR).
    // const IS_CLIENT = typeof window !== 'undefined';
    // const Fallback = (
    //
    //     <F msg="Loading…" />
    //
    // );
    //
    // let SuspenseWithTemporaryWorkaround;
    // if (IS_CLIENT) {
    //   const Dashboard = lazy(() => import('../dashboard/Dashboard'));
    //   SuspenseWithTemporaryWorkaround = (
    //     <Suspense fallback={Fallback}>
    //       <Dashboard />
    //     </Suspense>
    //   );
    // } else {
    //   SuspenseWithTemporaryWorkaround = Fallback;
    // }
    //
    // return SuspenseWithTemporaryWorkaround;
  }

  render() {
    // HACK(all-the-things): we can't get rid of FOUC in dev mode because we want hot reloading of CSS updates.
    // This hides the unsightly unstyled app. However, in dev mode, it removes the perceived gain of SSR. :-/
    const devOnlyHiddenOnLoadStyle = this.state.devOnlyHiddenOnLoad ? { opacity: 0 } : null;
    const closeAriaLabel = this.props.intl.formatMessage(messages.close);
    const closeButton = (
      <IconButton key="close" className="App-snackbar-icon" color="inherit" aria-label={closeAriaLabel}>
        <CloseIcon />
      </IconButton>
    );

    return (
      <UserContext.Provider value={this.state.userContext}>
        <SnackbarProvider action={[closeButton]}>
          <ErrorBoundary>

              <CssBaseline />

                <ScrollToTop>
                  <Switch>
                    <Route path={`/dashboard`} component={this.renderDashboard} />
                    <Route path={`/:username/search/:query`} component={Search} />
                    <Route path={`/:username/:section/:album/:name`} component={Content} />
                    <Route path={`/:username/:section/:name`} component={Content} />
                    <Route path={`/:username/:name`} component={Content} />
                    <Route path={`/:username`} component={Content} />
                    <Route path={`/`} component={Content} />
                  </Switch>
                </ScrollToTop>

          </ErrorBoundary>
        </SnackbarProvider>
      </UserContext.Provider>
    );
  }
}

@withRouter
class ScrollToTop extends Component {
  componentDidUpdate(prevProps) {
    if (this.props.location.pathname !== prevProps.location.pathname) {
      window.scrollTo(0, 0);
    }
  }

  render() {
    return this.props.children;
  }
}

export default App;
<commit_msg>router: take advantage of new react-router feature to use array for same component<commit_after>import './App.css';
import classNames from 'classnames';
import clientHealthCheck from './client_health_check';
import CloseIcon from '@material-ui/icons/Close';
import Content from '../content/Content';
import CssBaseline from '@material-ui/core/CssBaseline';
import Dashboard from '../dashboard/Dashboard';
import { defineMessages, injectIntl } from '../../shared/i18n';
import ErrorBoundary from '../error/ErrorBoundary';
import Footer from './Footer';
import Header from './Header';
import IconButton from '@material-ui/core/IconButton';
import { Route, Switch, withRouter } from 'react-router-dom';
import React, { Component } from 'react';
import Search from '../content/Search';
import { SnackbarProvider } from 'notistack';
import styles from './constants.module.css';
import UserContext from './User_Context';

const messages = defineMessages({
  close: { msg: 'Close' },
});

// This is the main entry point on the client-side.
@injectIntl
class App extends Component {
  constructor(props) {
    super(props);

    this.state = {
      userContext: {
        user: props.user,
      },
      devOnlyHiddenOnLoad: process.env.NODE_ENV === 'development',
    };
  }

  componentDidMount() {
    // Remove MaterialUI's SSR generated CSS.
    const jssStyles = document.getElementById('jss-ssr');
    if (jssStyles && jssStyles.parentNode) {
      jssStyles.parentNode.removeChild(jssStyles);
    }

    // Upon starting the app, kick off a client health check which runs periodically.
    clientHealthCheck();

    this.setState({ devOnlyHiddenOnLoad: false });
  }

  renderDashboard() {
    return <Dashboard />;
    // TODO(mime): Can't get Suspense/lazy to play nicely with CSS modules yet.
    // TODO(mime): Suspense and lazy aren't supported by ReactDOMServer yet (breaks SSR).
    // const IS_CLIENT = typeof window !== 'undefined';
    // const Fallback = (
    //
    //     <F msg="Loading…" />
    //
    // );
    //
    // let SuspenseWithTemporaryWorkaround;
    // if (IS_CLIENT) {
    //   const Dashboard = lazy(() => import('../dashboard/Dashboard'));
    //   SuspenseWithTemporaryWorkaround = (
    //     <Suspense fallback={Fallback}>
    //       <Dashboard />
    //     </Suspense>
    //   );
    // } else {
    //   SuspenseWithTemporaryWorkaround = Fallback;
    // }
    //
    // return SuspenseWithTemporaryWorkaround;
  }

  render() {
    // HACK(all-the-things): we can't get rid of FOUC in dev mode because we want hot reloading of CSS updates.
    // This hides the unsightly unstyled app. However, in dev mode, it removes the perceived gain of SSR. :-/
    const devOnlyHiddenOnLoadStyle = this.state.devOnlyHiddenOnLoad ? { opacity: 0 } : null;
    const closeAriaLabel = this.props.intl.formatMessage(messages.close);
    const closeButton = (
      <IconButton key="close" className="App-snackbar-icon" color="inherit" aria-label={closeAriaLabel}>
        <CloseIcon />
      </IconButton>
    );

    return (
      <UserContext.Provider value={this.state.userContext}>
        <SnackbarProvider action={[closeButton]}>
          <ErrorBoundary>

              <CssBaseline />

                <ScrollToTop>
                  <Switch>
                    <Route path={`/dashboard`} component={this.renderDashboard} />
                    <Route path={`/:username/search/:query`} component={Search} />
                    <Route
                      path={[
                        `/:username/:section/:album/:name`,
                        `/:username/:section/:name`,
                        `/:username/:name`,
                        `/:username`,
                        `/`,
                      ]}
                      component={Content}
                    />
                  </Switch>
                </ScrollToTop>

          </ErrorBoundary>
        </SnackbarProvider>
      </UserContext.Provider>
    );
  }
}

@withRouter
class ScrollToTop extends Component {
  componentDidUpdate(prevProps) {
    if (this.props.location.pathname !== prevProps.location.pathname) {
      window.scrollTo(0, 0);
    }
  }

  render() {
    return this.props.children;
  }
}

export default App;
<|endoftext|>


<commit_before>var app = angular.module('geoChat', ['ui.router', 'ngCookies', 'ngResource', 'ngSanitize','btford.socket-io'])
    .value('nickName', username);
var username;

app.factory('User', ['$http', function( $http) {
    return {
        addUser: function (user) {
            console.log(user);
            return $http({
                method: 'POST',
                url: '/api/add',
                data:user
            })
                .then(function (resp) {
                    return resp;
                });
        },

        getUser: function(){
            return $http({
                method: 'GET',
                url: '/api/add'
            })
                .then(function (resp) {
                    console.log(resp);
                    return resp.data;
                });
        }
    }
}]);

function statusChangeCallback(response) {
    if (response.status === 'connected') {
        FB.api('/me', function (response) {
            username = response.name;
        });
    }
}

function checkLoginState() {
    FB.getLoginStatus(function(response) {
      statusChangeCallback(response);
    });
  }

window.fbAsyncInit = function() {
  FB.init({
    appId      : '438180583036734',
    status     : true,
    xfbml      : true,  // parse social plugins on this page
    version    : 'v2.2'
  });

  FB.getLoginStatus(function(response) {
    statusChangeCallback(response);
  });

  FB.Event.subscribe('auth.login', function(resp) {
   window.location = 'http://chat-geo.herokuapp.com/#/home';
 });
    FB.Event.subscribe('auth.logout', function(resp) {
   window.location = 'https://chat-geo.herokuapp.com/';
 });

};
  // Load the SDK asynchronously
  (function(d, s, id) {
    var js, fjs = d.getElementsByTagName(s)[0];
    if (d.getElementById(id)){
      return;
    }
    js = d.createElement(s); js.id = id;
    js.src = "//connect.facebook.net/en_US/sdk.js";
    fjs.parentNode.insertBefore(js, fjs);
  }(document, 'script', 'facebook-jssdk'));

app.controller('AuthCtrl', ["$scope", "User", function ($scope, User) {
    $scope.username = [];
    $scope.FBLogin = function(){
        FB.login(function(response) {
            if (response.authResponse) {
                console.log('Welcome! Fetching your information... ');
                FB.api('/me', function(response) {
                    console.log('Good to see you, ' + response.name + '.');
                    var accessToken = FB.getAuthResponse().accessToken;
                    console.log(accessToken);
                    $scope.username.push({user:response.name});
                    console.log($scope.username);
                    User.addUser($scope.username)
                        .catch(function (error) {
                            console.log(error);
                        });

                    User.getUser()
                         .then(function (resData){
                             console.log(resData[1].user);
                             username = resData[1].user;
                        })
                        .catch(function (error){
                            console.log(error);
                        });
                });
     // $scope.$apply();
            } else {
                console.log('User cancelled login or did not fully authorize.');
            }
        }, {scope: ''});
    };
}]);

app.controller('SocketCtrl', function ($log, $scope, chatSocket, messageFormatter, nickName, $http) {
    $scope.newMessages = [];
    $scope.nickName = username;
    $scope.messageLog = 'Ready to chat!';

    $scope.FBLogout = function(){
        FB.logout(function(response) {
            console.log("You are logged out");
        });
    };

    $scope.sendMessage = function() {
    var match = $scope.message.match('^\/nick (.*)');
    if (angular.isDefined(match) && angular.isArray(match) && match.length === 2) {
      var oldNick = nickName;
      nickName = match[1];
      $scope.message = '';
        console.log($scope.messageLog);
      $scope.messageLog = messageFormatter(new Date(),
              nickName, 'nickname changed - from ' +
              oldNick + ' to ' + nickName + '!') + $scope.messageLog;
      $scope.nickName = nickName;
    }

    $log.debug('sending message', $scope.message);
    chatSocket.emit('message', nickName, $scope.message);
    $scope.message = '';
  };

  $scope.$on('socket:broadcast', function(event, data) {
    $log.debug('got a message', event.name);
    if (!data.payload) {
      $log.error('invalid message', 'event', event, 'data', JSON.stringify(data));
      return;
    }
    $scope.$apply(function() {

      //$scope.messageLog = $scope.messageLog + messageFormatter(new Date(), data.source, data.payload);
        $scope.messageLog = messageFormatter(new Date(), username, data.payload);

        $scope.newMessages.push($scope.messageLog);
        console.log(username)

    });
  });
});

app.factory('chatSocket', function (socketFactory) {
  var socket = socketFactory();
  socket.forward('broadcast');
  return socket;
});

app.value('messageFormatter', function(date, nick, message) {
  return date.toLocaleTimeString() + ' - ' +
      nick + ' - ' +
      message + '\n';

});

app.config(function($stateProvider, $urlRouterProvider){
  $urlRouterProvider.otherwise('/');

  $stateProvider
      .state('login', {
        url: '/',
        templateUrl: 'app/auth/login.html',
        controller: 'SocketCtrl'
      });

  $stateProvider
    .state('home', {
      url: '/home',
      templateUrl: 'app/chat/chat.html',
      controller: 'SocketCtrl'
  })
});
<commit_msg>changing the usernme<commit_after>var app = angular.module('geoChat', ['ui.router', 'ngCookies', 'ngResource', 'ngSanitize','btford.socket-io'])
    .value('nickName', username);
var username;

app.factory('User', ['$http', function( $http) {
    return {
        addUser: function (user) {
            console.log(user);
            return $http({
                method: 'POST',
                url: '/api/add',
                data:user
            })
                .then(function (resp) {
                    return resp;
                });
        },

        getUser: function(){
            return $http({
                method: 'GET',
                url: '/api/add'
            })
                .then(function (resp) {
                    console.log(resp);
                    return resp.data;
                });
        }
    }
}]);

function statusChangeCallback(response) {
    if (response.status === 'connected') {
        FB.api('/me', function (response) {
            username = response.name;
            console.log(username);
        });
    }
}

function checkLoginState() {
    FB.getLoginStatus(function(response) {
      statusChangeCallback(response);
    });
  }

window.fbAsyncInit = function() {
  FB.init({
    appId      : '438180583036734',
    status     : true,
    xfbml      : true,  // parse social plugins on this page
    version    : 'v2.2'
  });

  FB.getLoginStatus(function(response) {
    statusChangeCallback(response);
  });

  FB.Event.subscribe('auth.login', function(resp) {
   window.location = 'http://chat-geo.herokuapp.com/#/home';
 });
    FB.Event.subscribe('auth.logout', function(resp) {
   window.location = 'https://chat-geo.herokuapp.com/';
 });

};
  // Load the SDK asynchronously
  (function(d, s, id) {
    var js, fjs = d.getElementsByTagName(s)[0];
    if (d.getElementById(id)){
      return;
    }
    js = d.createElement(s); js.id = id;
    js.src = "//connect.facebook.net/en_US/sdk.js";
    fjs.parentNode.insertBefore(js, fjs);
  }(document, 'script', 'facebook-jssdk'));

app.controller('AuthCtrl', ["$scope", "User", function ($scope, User) {
    $scope.username = [];
    $scope.FBLogin = function(){
        FB.login(function(response) {
            if (response.authResponse) {
                console.log('Welcome! Fetching your information... ');
                FB.api('/me', function(response) {
                    console.log('Good to see you, ' + response.name + '.');
                    var accessToken = FB.getAuthResponse().accessToken;
                    console.log(accessToken);
                    $scope.username.push({user:response.name});
                    console.log($scope.username);
                    User.addUser($scope.username)
                        .catch(function (error) {
                            console.log(error);
                        });

                    User.getUser()
                         .then(function (resData){
                             console.log(resData[1].user);
                             username = resData[1].user;
                             console.log(resData);
                        })
                        .catch(function (error){
                            console.log(error);
                        });
                });
     // $scope.$apply();
            } else {
                console.log('User cancelled login or did not fully authorize.');
            }
        }, {scope: ''});
    };
}]);

app.controller('SocketCtrl', function ($log, $scope, chatSocket, messageFormatter, nickName, $http) {
    $scope.newMessages = [];
    $scope.nickName = username;
    $scope.messageLog = 'Ready to chat!';

    $scope.FBLogout = function(){
        FB.logout(function(response) {
            console.log("You are logged out");
        });
    };

    $scope.sendMessage = function() {
    var match = $scope.message.match('^\/nick (.*)');
    if (angular.isDefined(match) && angular.isArray(match) && match.length === 2) {
      var oldNick = nickName;
      nickName = match[1];
      $scope.message = '';
        console.log($scope.messageLog);
      $scope.messageLog = messageFormatter(new Date(),
              nickName, 'nickname changed - from ' +
              oldNick + ' to ' + nickName + '!') + $scope.messageLog;
      $scope.nickName = nickName;
    }

    $log.debug('sending message', $scope.message);
    chatSocket.emit('message', nickName, $scope.message);
    $scope.message = '';
  };

  $scope.$on('socket:broadcast', function(event, data) {
    $log.debug('got a message', event.name);
    if (!data.payload) {
      $log.error('invalid message', 'event', event, 'data', JSON.stringify(data));
      return;
    }
    $scope.$apply(function() {

      //$scope.messageLog = $scope.messageLog + messageFormatter(new Date(), data.source, data.payload);
        $scope.messageLog = messageFormatter(new Date(), username, data.payload);

        $scope.newMessages.push($scope.messageLog);
        console.log(username)

    });
  });
});

app.factory('chatSocket', function (socketFactory) {
  var socket = socketFactory();
  socket.forward('broadcast');
  return socket;
});

app.value('messageFormatter', function(date, nick, message) {
  return date.toLocaleTimeString() + ' - ' +
      nick + ' - ' +
      message + '\n';

});

app.config(function($stateProvider, $urlRouterProvider){
  $urlRouterProvider.otherwise('/');

  $stateProvider
      .state('login', {
        url: '/',
        templateUrl: 'app/auth/login.html',
        controller: 'SocketCtrl'
      });

  $stateProvider
    .state('home', {
      url: '/home',
      templateUrl: 'app/chat/chat.html',
      controller: 'SocketCtrl'
  })
});
<|endoftext|>


<commit_before>const { createRunner } = require('atom-jasmine3-test-runner')

module.exports = createRunner({
	specHelper: {
		attachToDom: true,
	},
	testPackages: ['minimap'],
})
<commit_msg>chore(ci): use ci spec helper<commit_after>const { createRunner } = require('atom-jasmine3-test-runner')

module.exports = createRunner({
	specHelper: {
		attachToDom: true,
		ci: true,
	},
	testPackages: ['minimap'],
})
<|endoftext|>