<commit_before>describe("chorus.views.DashboardProjectList", function() {
    beforeEach(function() {
        // Has a summary, but no insights, and user is a member
        this.workspace1 = backboneFixtures.workspace({
            name: "Broccoli",
            owner: { firstName: 'Green', lastName: 'Giant' },
            latestCommentList: [],
            summary: 'We are making sails!',
            isMember: true,
            datasetsCount: 1,
            membersCount: 1,
            workfilesCount: 1,
            insightsCount: 1,
            recentInsightsCount: 1,
            recentCommentsCount: 1
        });
        delete this.workspace1.attributes.latestInsight;

        // One insight, no summary, and the user is not a member
        this.workspace2 = backboneFixtures.workspace({
            name: "Camels",
            owner: { firstName: 'Andre', lastName: 'The Giant' },
            latestCommentList: [],
            numberOfInsights: 1,
            isMember: false,
            datasetsCount: 2,
            membersCount: 2,
            workfilesCount: 2,
            insightsCount: 2,
            recentInsightsCount: 2,
            recentCommentsCount: 2
        });
        delete this.workspace2.attributes.summary;
        spyOn(this.workspace2, 'latestInsight').andReturn(backboneFixtures.activity.insightOnGreenplumDataSource());

        // Three insights and a summary, and user is a member
        this.workspace3 = backboneFixtures.workspace({numberOfInsights: 3, isMember: true});
        spyOn(this.workspace3, 'latestInsight').andReturn(backboneFixtures.activity.insightOnGreenplumDataSource());

        this.collection = new chorus.collections.WorkspaceSet([this.workspace1, this.workspace2, this.workspace3]);
        this.collection.loaded = true;
        this.view = new chorus.views.DashboardProjectList({collection: this.collection});
    });

    describe("#render", function() {
        beforeEach(function() {
            this.view.noFilter = true;
            this.view.render();
        });

        it("displays the name of the workspace as a link", function() {
            expect(this.view.$(".project_name span").eq(0).text()).toBe("Broccoli");
            expect(this.view.$(".project_name").eq(0).attr('href')).toBe(this.workspace1.showUrl());

            expect(this.view.$(".project_name span").eq(1).text()).toBe("Camels");
            expect(this.view.$(".project_name").eq(1).attr('href')).toBe(this.workspace2.showUrl());
        });

        describe("data row section", function () {
            it("displays the statistics of each workspace", function() {
                expect(this.view.$(".data_row.first .left_side .number").eq(0).text()).toBe("1");
                expect(this.view.$(".data_row.first .left_side .number").eq(1).text()).toBe("2");

                expect(this.view.$(".data_row.first .left_side .element").eq(0).text()).toBe("Insight");
                expect(this.view.$(".data_row.first .left_side .element").eq(1).text()).toBe("Insights");

                expect(this.view.$(".data_row.first .right_side .number").eq(0).text()).toBe("1");
                expect(this.view.$(".data_row.first .right_side .number").eq(1).text()).toBe("2");

                expect(this.view.$(".data_row.first .right_side .element").eq(0).text()).toBe("Workfile");
                expect(this.view.$(".data_row.first .right_side .element").eq(1).text()).toBe("Workfiles");

                expect(this.view.$(".data_row.last .left_side .number").eq(0).text()).toBe("1");
                expect(this.view.$(".data_row.last .left_side .number").eq(1).text()).toBe("2");

                expect(this.view.$(".data_row.last .left_side .element").eq(0).text()).toBe("Dataset");
                expect(this.view.$(".data_row.last .left_side .element").eq(1).text()).toBe("Datasets");

                expect(this.view.$(".data_row.last .right_side .number").eq(0).text()).toBe("1");
                expect(this.view.$(".data_row.last .right_side .number").eq(1).text()).toBe("2");

                expect(this.view.$(".data_row.last .right_side .element").eq(0).text()).toBe("Member");
                expect(this.view.$(".data_row.last .right_side .element").eq(1).text()).toBe("Members");
            });
        });

        describe("latest insight section", function () {
            it("shows the counts for latest comments and insights", function () {
                expect(this.view.$(".insight_row .main_row").eq(0).text().trim()).toBe("1 Comment and 1 Insight");
                expect(this.view.$(".insight_row .main_row").eq(1).text().trim()).toBe("2 Comments and 2 Insights");
            });

            it("shows the counts for latest comments and insights", function () {
                expect(this.view.$(".insight_row .sub_row").eq(0).text().trim()).toBe("in the last 7 days");
            });
        });

        /*
        describe("filtering", function () {
            context("when filter:members_only is triggered on the collection", function () {
                beforeEach(function () {
                    this.collection.trigger('filter:members_only');
                    this.renderedProjects = _.map(this.view.projectCards, function (view) {
                        return view.model;
                    });
                });

                it("renders only cards for workspaces that the current user is a member of", function () {
                    expect(this.renderedProjects).toContain(this.workspace1);
                    expect(this.renderedProjects).not.toContain(this.workspace2);
                    expect(this.renderedProjects).toContain(this.workspace3);
                });

                context("and then filter:all is triggered on the collection", function () {
                    beforeEach(function () {
                        this.collection.trigger('filter:all');
                        this.renderedProjects = _.map(this.view.projectCards, function (view) {
                            return view.model;
                        });
                    });

                    it("renders cards for all workspaces in the collection", function () {
                        expect(this.renderedProjects).toContain(this.workspace1);
                        expect(this.renderedProjects).toContain(this.workspace2);
                        expect(this.renderedProjects).toContain(this.workspace3);
                    });
                });
            });
        });
        */
    });
});<commit_msg>additionl revisions to the spec for the stylin' updates.<commit_after>describe("chorus.views.DashboardProjectList", function() {
    beforeEach(function() {
        // Has a summary, but no insights, and user is a member
        this.workspace1 = backboneFixtures.workspace({
            name: "Broccoli",
            owner: { firstName: 'Green', lastName: 'Giant' },
            latestCommentList: [],
            summary: 'We are making sails!',
            isMember: true,
            datasetsCount: 1,
            membersCount: 1,
            workfilesCount: 1,
            insightsCount: 1,
            recentInsightsCount: 1,
            recentCommentsCount: 1
        });
        delete this.workspace1.attributes.latestInsight;

        // One insight, no summary, and the user is not a member
        this.workspace2 = backboneFixtures.workspace({
            name: "Camels",
            owner: { firstName: 'Andre', lastName: 'The Giant' },
            latestCommentList: [],
            numberOfInsights: 1,
            isMember: false,
            datasetsCount: 2,
            membersCount: 2,
            workfilesCount: 2,
            insightsCount: 2,
            recentInsightsCount: 2,
            recentCommentsCount: 2
        });
        delete this.workspace2.attributes.summary;
        spyOn(this.workspace2, 'latestInsight').andReturn(backboneFixtures.activity.insightOnGreenplumDataSource());

        // Three insights and a summary, and user is a member
        this.workspace3 = backboneFixtures.workspace({numberOfInsights: 3, isMember: true});
        spyOn(this.workspace3, 'latestInsight').andReturn(backboneFixtures.activity.insightOnGreenplumDataSource());

        this.collection = new chorus.collections.WorkspaceSet([this.workspace1, this.workspace2, this.workspace3]);
        this.collection.loaded = true;
        this.view = new chorus.views.DashboardProjectList({collection: this.collection});
    });

    describe("#render", function() {
        beforeEach(function() {
            this.view.noFilter = true;
            this.view.render();
        });

        it("displays the name of the workspace as a link", function() {
            expect(this.view.$(".project_name span").eq(0).text()).toBe("Broccoli");
            expect(this.view.$(".project_name").eq(0).attr('href')).toBe(this.workspace1.showUrl());

            expect(this.view.$(".project_name span").eq(1).text()).toBe("Camels");
            expect(this.view.$(".project_name").eq(1).attr('href')).toBe(this.workspace2.showUrl());
        });

        describe("data row section", function () {
            it("displays the statistics of each workspace", function() {
                expect(this.view.$(".data_row.first .left_side .number .insight_link").eq(0).text()).toBe("1");
                expect(this.view.$(".data_row.first .left_side .number .insight_link").eq(1).text()).toBe("2");

                expect(this.view.$(".data_row.first .left_side .element").eq(0).text()).toBe("Insight");
                expect(this.view.$(".data_row.first .left_side .element").eq(1).text()).toBe("Insights");

                expect(this.view.$(".data_row.first .right_side .number .workfile_link").eq(0).text()).toBe("1");
                expect(this.view.$(".data_row.first .right_side .number .workfile_link").eq(1).text()).toBe("2");

                expect(this.view.$(".data_row.first .right_side .element").eq(0).text()).toBe("Workfile");
                expect(this.view.$(".data_row.first .right_side .element").eq(1).text()).toBe("Workfiles");

                expect(this.view.$(".data_row.last .left_side .number .dataset_list").eq(0).text()).toBe("1");
                expect(this.view.$(".data_row.last .left_side .number .dataset_list").eq(1).text()).toBe("2");

                expect(this.view.$(".data_row.last .left_side .element").eq(0).text()).toBe("Dataset");
                expect(this.view.$(".data_row.last .left_side .element").eq(1).text()).toBe("Datasets");

                expect(this.view.$(".data_row.last .right_side .number .member_list").eq(0).text()).toBe("1");
                expect(this.view.$(".data_row.last .right_side .number .member_list").eq(1).text()).toBe("2");

                expect(this.view.$(".data_row.last .right_side .element").eq(0).text()).toBe("Member");
                expect(this.view.$(".data_row.last .right_side .element").eq(1).text()).toBe("Members");
            });
        });

        describe("latest insight section", function () {
            it("shows the counts for latest comments and insights", function () {
                expect(this.view.$(".insight_row .main_row").eq(0).text().trim()).toBe("1 Comment and 1 Insight");
                expect(this.view.$(".insight_row .main_row").eq(1).text().trim()).toBe("2 Comments and 2 Insights");
            });

            it("shows the counts for latest comments and insights", function () {
                expect(this.view.$(".insight_row .sub_row").eq(0).text().trim()).toBe("in the last 7 days");
            });
        });

        /*
        describe("filtering", function () {
            context("when filter:members_only is triggered on the collection", function () {
                beforeEach(function () {
                    this.collection.trigger('filter:members_only');
                    this.renderedProjects = _.map(this.view.projectCards, function (view) {
                        return view.model;
                    });
                });

                it("renders only cards for workspaces that the current user is a member of", function () {
                    expect(this.renderedProjects).toContain(this.workspace1);
                    expect(this.renderedProjects).not.toContain(this.workspace2);
                    expect(this.renderedProjects).toContain(this.workspace3);
                });

                context("and then filter:all is triggered on the collection", function () {
                    beforeEach(function () {
                        this.collection.trigger('filter:all');
                        this.renderedProjects = _.map(this.view.projectCards, function (view) {
                            return view.model;
                        });
                    });

                    it("renders cards for all workspaces in the collection", function () {
                        expect(this.renderedProjects).toContain(this.workspace1);
                        expect(this.renderedProjects).toContain(this.workspace2);
                        expect(this.renderedProjects).toContain(this.workspace3);
                    });
                });
            });
        });
        */
    });
});<|endoftext|>


<commit_before>// -------------------------------------
//
//   Gulpfile
//
// -------------------------------------
//
// Available tasks:
//   `gulp`
//   'gulp browser-sync'
//   `gulp build`
//   `gulp build:dev`
//   `gulp clean`
//   `gulp clean:css`
//   `gulp clean:styleguide`
//   `gulp compile:sass`
//   `gulp compile:styleguide`
//   `gulp lint:js`
//   `gulp lint:css`
//   `gulp minify:css`
//   `gulp serve`
//   `gulp test:css`
//   `gulp watch`
//   `gulp watch:js`
//   `gulp watch:sass`
//   `gulp watch:styleguide`
//
// -------------------------------------

// -------------------------------------
//   Modules
// -------------------------------------
//
// gulp              : The streaming build system
// gulp-autoprefixer : Prefix CSS
// gulp-concat       : Concatenate files
// gulp-clean-css    : Minify CSS
// gulp-load-plugins : Automatically load Gulp plugins
// gulp-parker       : Stylesheet analysis tool
// gulp-plumber      : Prevent pipe breaking from errors
// gulp-rename       : Rename files
// gulp-sass         : Compile Sass
// gulp-sass-glob    : Provide Sass Globbing
// gulp-sass-lint    : Lint Sass
// gulp-size         : Print file sizes
// gulp-sourcemaps   : Generate sourcemaps
// gulp-uglify       : Minify JavaScript with UglifyJS
// gulp-util         : Utility functions
// gulp-watch        : Watch stream
// browser-sync      : Device and browser testing tool
// del               : delete
// eslint            : JavaScript code quality tool
// run-sequence      : Run a series of dependent Gulp tasks in order
// -------------------------------------

// -------------------------------------
//   Front-End Dependencies
// -------------------------------------
// breakpoint-sass       : Really Simple Media Queries with Sass
// node-sass             : Wrapper around libsass
// node-sass-import-once : Custom importer for node-sass that only allows a file to be imported once
// susy                  : Sass power-tools for web layout
// typey                 : A complete framework for working with typography in sass
// -------------------------------------

/* global require */

var gulp = require('gulp');
// Setting pattern this way allows non gulp- plugins to be loaded as well.
var plugins = require('gulp-load-plugins')({
  pattern: '*',
  rename: {
    'node-sass-import-once': 'importOnce',
    'gulp-sass-glob': 'sassGlob',
    'run-sequence': 'runSequence',
    'gulp-clean-css': 'cleanCSS',
    'gulp-stylelint': 'stylelint',
    'gulp-babel': 'babel'
  }
});

// Used to generate relative paths for style guide output.
var path = require('path');

// These are used in the options below.
var paths = {
  styles: {
    source: 'sass/',
    destination: 'css/dist'
  },
  scripts: {
    source: 'js/src',
    destination: 'js/dist'
  },
  images: 'img/',
  styleGuide: 'styleguide'
};

// These are passed to each task.
var options = {

  // ----- Browsersync ----- //

  browserSync: {
    // Put your local site URL here to prevent Browsersync
    // from prompting you to add additional scripts to your page.
    // proxy: {
    //   target: 'http://local.example.com'
    // },
    open: 'external',
    xip: true,
    logConnections: true
  },

  // ----- CSS ----- //

  css: {
    files: path.join(paths.styles.destination, '**/*.css'),
    file: path.join(paths.styles.destination, '/styles.css'),
    destination: path.join(paths.styles.destination)
  },

  // ----- Sass ----- //

  sass: {
    files: path.join(paths.styles.source, '**/*.scss'),
    file: path.join(paths.styles.source, 'styles.scss'),
    destination: path.join(paths.styles.destination)
  },

  // ----- JS ----- //
  js: {
    files: path.join(paths.scripts.source, '**/*.js'),
    destination: path.join(paths.scripts.destination)
  },

  // ----- Images ----- //
  images: {
    files: paths.images + '**/*.{png,gif,jpg,svg}',
    destination: paths.images
  },

  // ----- eslint ----- //
  jsLinting: {
    files: {
      theme: [
        paths.scripts + '**/*.js',
        '!' + paths.scripts + '**/*.min.js'
      ],
      gulp: [
        'gulpfile.js',
        'gulp-tasks/**/*'
      ]
    }

  },
};

// Tasks
require('./gulp-tasks/browser-sync')(gulp, plugins, options);
require('./gulp-tasks/build')(gulp, plugins, options);
require('./gulp-tasks/clean')(gulp, plugins, options);
require('./gulp-tasks/clean-css')(gulp, plugins, options);
require('./gulp-tasks/clean-styleguide')(gulp, plugins, options);
require('./gulp-tasks/compile-sass')(gulp, plugins, options);
require('./gulp-tasks/compile-js')(gulp, plugins, options);
require('./gulp-tasks/compile-styleguide')(gulp, plugins, options);
require('./gulp-tasks/default')(gulp, plugins, options);
require('./gulp-tasks/lint-js')(gulp, plugins, options);
require('./gulp-tasks/lint-css')(gulp, plugins, options);
require('./gulp-tasks/minify-css')(gulp, plugins, options);
require('./gulp-tasks/serve')(gulp, plugins, options);
require('./gulp-tasks/test-css')(gulp, plugins, options);
require('./gulp-tasks/watch')(gulp, plugins, options);

// Credits:
//
// - http://drewbarontini.com/articles/building-a-better-gulpfile/
// - https://teamgaslight.com/blog/small-sips-of-gulp-dot-js-4-steps-to-reduce-complexity
// - http://cgit.drupalcode.org/zen/tree/STARTERKIT/gulpfile.js?h=7.x-6.x
// - https://github.com/google/web-starter-kit/blob/master/gulpfile.js
<commit_msg>editing gulpfile<commit_after>// -------------------------------------
//
//   Gulpfile
//
// -------------------------------------
//
// Available tasks:
//   `gulp`
//   'gulp browser-sync'
//   `gulp build`
//   `gulp build:dev`
//   `gulp clean`
//   `gulp clean:css`
//   `gulp compile:sass`
//   `gulp lint:js`
//   `gulp lint:css`
//   `gulp minify:css`
//   `gulp serve`
//   `gulp test:css`
//   `gulp watch`
//   `gulp watch:js`
//   `gulp watch:sass`
//
// -------------------------------------

// -------------------------------------
//   Modules
// -------------------------------------
//
// gulp              : The streaming build system
// gulp-autoprefixer : Prefix CSS
// gulp-concat       : Concatenate files
// gulp-clean-css    : Minify CSS
// gulp-load-plugins : Automatically load Gulp plugins
// gulp-parker       : Stylesheet analysis tool
// gulp-plumber      : Prevent pipe breaking from errors
// gulp-rename       : Rename files
// gulp-sass         : Compile Sass
// gulp-sass-glob    : Provide Sass Globbing
// gulp-sass-lint    : Lint Sass
// gulp-size         : Print file sizes
// gulp-sourcemaps   : Generate sourcemaps
// gulp-uglify       : Minify JavaScript with UglifyJS
// gulp-util         : Utility functions
// gulp-watch        : Watch stream
// browser-sync      : Device and browser testing tool
// del               : delete
// eslint            : JavaScript code quality tool
// run-sequence      : Run a series of dependent Gulp tasks in order
// -------------------------------------

// -------------------------------------
//   Front-End Dependencies
// -------------------------------------
// breakpoint-sass       : Really Simple Media Queries with Sass
// node-sass             : Wrapper around libsass
// node-sass-import-once : Custom importer for node-sass that only allows a file to be imported once
// susy                  : Sass power-tools for web layout
// typey                 : A complete framework for working with typography in sass
// -------------------------------------

/* global require */

var gulp = require('gulp');
// Setting pattern this way allows non gulp- plugins to be loaded as well.
var plugins = require('gulp-load-plugins')({
  pattern: '*',
  rename: {
    'node-sass-import-once': 'importOnce',
    'gulp-sass-glob': 'sassGlob',
    'run-sequence': 'runSequence',
    'gulp-clean-css': 'cleanCSS',
    'gulp-stylelint': 'stylelint',
    'gulp-babel': 'babel'
  }
});

// Used to generate relative paths for style guide output.
var path = require('path');

// These are used in the options below.
var paths = {
  styles: {
    source: 'sass/',
    destination: 'css/dist'
  },
  scripts: {
    source: 'js/src',
    destination: 'js/dist'
  }
};

// These are passed to each task.
var options = {

  // ----- Browsersync ----- //

  browserSync: {
    // Put your local site URL here to prevent Browsersync
    // from prompting you to add additional scripts to your page.
    // proxy: {
    //   target: 'http://local.example.com'
    // },
    open: 'external',
    xip: true,
    logConnections: true
  },

  // ----- CSS ----- //

  css: {
    files: path.join(paths.styles.destination, '**/*.css'),
    file: path.join(paths.styles.destination, '/styles.css'),
    destination: path.join(paths.styles.destination)
  },

  // ----- Sass ----- //

  sass: {
    files: path.join(paths.styles.source, '**/*.scss'),
    file: path.join(paths.styles.source, 'styles.scss'),
    destination: path.join(paths.styles.destination)
  },

  // ----- JS ----- //
  js: {
    files: path.join(paths.scripts.source, '**/*.js'),
    destination: path.join(paths.scripts.destination)
  },

  // ----- Images ----- //
  images: {
    files: paths.images + '**/*.{png,gif,jpg,svg}',
    destination: paths.images
  },

  // ----- eslint ----- //
  jsLinting: {
    files: {
      theme: [
        paths.scripts + '**/*.js',
        '!' + paths.scripts + '**/*.min.js'
      ],
      gulp: [
        'gulpfile.js',
        'gulp-tasks/**/*'
      ]
    }

  },
};

// Tasks
require('./gulp-tasks/browser-sync')(gulp, plugins, options);
require('./gulp-tasks/build')(gulp, plugins, options);
require('./gulp-tasks/clean')(gulp, plugins, options);
require('./gulp-tasks/clean-css')(gulp, plugins, options);
require('./gulp-tasks/compile-sass')(gulp, plugins, options);
require('./gulp-tasks/compile-js')(gulp, plugins, options);
require('./gulp-tasks/default')(gulp, plugins, options);
require('./gulp-tasks/lint-js')(gulp, plugins, options);
require('./gulp-tasks/lint-css')(gulp, plugins, options);
require('./gulp-tasks/minify-css')(gulp, plugins, options);
require('./gulp-tasks/serve')(gulp, plugins, options);
require('./gulp-tasks/test-css')(gulp, plugins, options);
require('./gulp-tasks/watch')(gulp, plugins, options);

// Credits:
//
// - http://drewbarontini.com/articles/building-a-better-gulpfile/
// - https://teamgaslight.com/blog/small-sips-of-gulp-dot-js-4-steps-to-reduce-complexity
// - http://cgit.drupalcode.org/zen/tree/STARTERKIT/gulpfile.js?h=7.x-6.x
// - https://github.com/google/web-starter-kit/blob/master/gulpfile.js
<|endoftext|>


<commit_before>'use strict';

let app = require('../../../server');
let dbConfig = require('elyoos-server-lib').databaseConfig;
let elyoosTestUtil = require('elyoos-server-test-util');

elyoosTestUtil.init(require('elyoos-server-lib'), app);

elyoosTestUtil.stubCDN().stub(require('../../../models/util/cdn'));
elyoosTestUtil.stubEmailQueue();
elyoosTestUtil.stubLimitRate();
elyoosTestUtil.stubRecaptcha.stub(require('../../../models/util/recaptcha'));

describe('Initialize Server for all integration tests', function () {

    before(function (done) {
        app.on('start', function () {
            dbConfig.connected.then(function () {
                done();
            });
        });
    });

    it('dummy Test', function () {
    });
});
<commit_msg>Bugfix: get cdn from commonLib<commit_after>'use strict';

let app = require('../../../server');
let dbConfig = require('elyoos-server-lib').databaseConfig;
let elyoosTestUtil = require('elyoos-server-test-util');

elyoosTestUtil.init(require('elyoos-server-lib'), app);

elyoosTestUtil.stubCDN().stub(require('elyoos-server-lib').cdn);
elyoosTestUtil.stubEmailQueue();
elyoosTestUtil.stubLimitRate();
elyoosTestUtil.stubRecaptcha.stub(require('../../../models/util/recaptcha'));

describe('Initialize Server for all integration tests', function () {

    before(function (done) {
        app.on('start', function () {
            dbConfig.connected.then(function () {
                done();
            });
        });
    });

    it('dummy Test', function () {
    });
});
<|endoftext|>


<commit_before><commit_msg>Refactored get asset<commit_after><|endoftext|>


<commit_before>const createAuthMiddleware = () => store => next => action => next(action)

export default createAuthMiddleware
<commit_msg>Add storage option to middleware<commit_after>import createLocalStorageStore from './sessionStores/localStorage'

const createAuthMiddleware = (config = {}) => {
  const storage = config.storage || createLocalStorageStore()

  return store => next => action => next(action)
}

export default createAuthMiddleware
<|endoftext|>


<commit_before>export default class AddController {
  constructor($scope, $http, staffnewSvc, templateSvc, $q, storeSvc) {
    "ngInject";
    //var vm = this;

    this.name = 'add';
    this.staffnewSvc = staffnewSvc;
    this.templateSvc = templateSvc;
    this.storeSvc = storeSvc;
    this.scope = $scope;
    this.q = $q;
    this.d = {};
    this.getTempList();
    this.gettemp = {
      role_ids: '',
      role_info: ''
    }
    this.options = {
    }

    this.users = {
      name: '',
      contact: '',
      rtx: '',//即时通讯账号
      storeId: '', //门店Id
      storeName: '', //门店名称
      role_ids: '', //模板ID
      templateName: '', // '模板名称', //模板名称
      password: '', //密码
      con_password: '',//确认密码
      gender: '', //性别
      status_id: ''//状态
    };
    this.staffstatus = [{ id: 1, name: '正常' },{ id: 3, name: '冻结' }];
    this.sex = [{ id: 1, name: '男' }, { id: 3, name: '女' }];
    this.selectSr = {
      status: this.staffstatus[0]
    }
    this.selectSex = {
      sex: this.sex[0]
    }
  }

  $onInit() {
    this.getStorelist().then(result => {
      this.storelist = result;
    })
  }

  /**
   * [create 提交]
   */
  createuser() {
    var users = this.users,
      options = this.options,
      gettemp = this.gettemp,
      selectSr = this.selectSr,
      selectSex = this.selectSex
      if(options.store== undefined){
        alert("请选择门店！")
        return false;
      }else{
        users.storeName = options.store.name;
        users.storeId = options.store.organization_id;
      }

      users.role_ids = gettemp.role_ids;
      users.role_info = gettemp.role_info;
      users.status_id = selectSr.status.id;
      users.gender = selectSex.sex.name;

       if(gettemp.role_ids=="")
        {
          alert ("请选择功能模板")
          return false;
        }
        else{
          this.staffnewSvc.createuser(users)
          .then(data => {
          //跳转到员工list页面
          this.staffnewSvc.getstafflist();
        });

        }
  }

  //获取模板名称
  getTemplateList(temlateName) {
    let deferred = this.q.defer();
    let templateList = this.templateSvc.getPageTempbyname(temlateName).then((result) => {
      return result.datas;
    });
    deferred.resolve(templateList);
    return deferred.promise;
  }

  //获取功能模板 check 多选
  getTempList() {
    this.loadPromise = this.templateSvc.getPageAllTempList({ limit: 999999 })
    this.loadPromise.then((result) => {
      this.scope.datas = result.datas;
    });
  }

  //获取门店list
  getStorelist(storename) {
    let deferred = this.q.defer();
    let storeList = this.storeSvc.getstorebyname(storename).then((result) => {
      return result.datas;
    });
    deferred.resolve(storeList);
    return deferred.promise;
  }

  //判断checked 被选中
  updateSelection($event, id, name) {
    var gettemp = this.gettemp
    var checkbox = $event.target;
    var action = (checkbox.checked ? 'add' : 'remove');
    //如果action 是add 则添加，要是remove 就是删除
    if (action == "add") {
      if (gettemp.role_ids == "") {
        gettemp.role_ids = id;
        gettemp.role_info = name;
      } else {
        gettemp.role_ids = gettemp.role_ids + "," + id;
        gettemp.role_info = gettemp.role_info + "," + name;
      }
    }
    else {
      // 取消选中，则删除当前的id和template name
      var temp_name = gettemp.role_info.split(',')
      var words = gettemp.role_ids.split(',')
      for (var i = 0; i < words.length; i++) {

        if (words[i] == id) {
          words.splice(i, 1);
          temp_name.splice(i, 1);
        }
      }
      gettemp.role_ids = words.join(",")
      gettemp.role_info = temp_name.join(",")
    }

    //console.log(gettemp.role_ids);
  }

}<commit_msg>fix rtx liyn<commit_after>export default class AddController {
  constructor($scope, $http, staffnewSvc, templateSvc, $q, storeSvc) {
    "ngInject";
    //var vm = this;

    this.name = 'add';
    this.staffnewSvc = staffnewSvc;
    this.templateSvc = templateSvc;
    this.storeSvc = storeSvc;
    this.scope = $scope;
    this.q = $q;
    this.d = {};
    this.getTempList();
    this.gettemp = {
      role_ids: '',
      role_info: ''
    }
    this.options = {
    }

    this.users = {
      name: '',
      contact: '',
      rtx: '',//即时通讯账号
      storeId: '', //门店Id
      storeName: '', //门店名称
      role_ids: '', //模板ID
      templateName: '', // '模板名称', //模板名称
      password: '', //密码
      con_password: '',//确认密码
      gender: '', //性别
      status_id: ''//状态
    };
    this.staffstatus = [{ id: 1, name: '正常' },{ id: 3, name: '冻结' }];
    this.sex = [{ id: 1, name: '男' }, { id: 3, name: '女' }];
    this.selectSr = {
      status: this.staffstatus[0]
    }
    this.selectSex = {
      sex: this.sex[0]
    }
  }

  $onInit() {
    this.getStorelist().then(result => {
      this.storelist = result;
    })
  }

  /**
   * [create 提交]
   */
  createuser() {
    var users = this.users,
      options = this.options,
      gettemp = this.gettemp,
      selectSr = this.selectSr,
      selectSex = this.selectSex
      if(options.store== undefined){
        alert("请选择门店！")
        return false;
      }else{
        users.storeName = options.store.name;
        users.storeId = options.store.organization_id;
      }

      users.role_ids = gettemp.role_ids;
      users.role_info = gettemp.role_info;
      users.status_id = selectSr.status.id;
      users.gender = selectSex.sex.name;
      users.im=users.rtx;
      console.log(users.rtx)
      //vm.users.rtx

       if(gettemp.role_ids=="")
        {
          alert ("请选择功能模板")
          return false;
        }
        else{
      console.log(users);
          this.staffnewSvc.createuser(users)
          .then(success => {

          //跳转到员工list页面
          this.staffnewSvc.getstafflist();
        });

        }
  }

  //获取模板名称
  getTemplateList(temlateName) {
    let deferred = this.q.defer();
    let templateList = this.templateSvc.getPageTempbyname(temlateName).then((result) => {
      return result.datas;
    });
    deferred.resolve(templateList);
    return deferred.promise;
  }

  //获取功能模板 check 多选
  getTempList() {
    this.loadPromise = this.templateSvc.getPageAllTempList({
     limit: 999999,
     status:1
    })
    this.loadPromise.then((result) => {
      this.scope.datas = result.datas;
    });
  }

  //获取门店list
  getStorelist(storename) {
    let deferred = this.q.defer();
    let storeList = this.storeSvc.getstorebyname(storename).then((result) => {
      return result.datas;
    });
    deferred.resolve(storeList);
    return deferred.promise;
  }

  //判断checked 被选中
  updateSelection($event, id, name) {
    var gettemp = this.gettemp
    var checkbox = $event.target;
    var action = (checkbox.checked ? 'add' : 'remove');
    //如果action 是add 则添加，要是remove 就是删除
    if (action == "add") {
      if (gettemp.role_ids == "") {
        gettemp.role_ids = id;
        gettemp.role_info = name;
      } else {
        gettemp.role_ids = gettemp.role_ids + "," + id;
        gettemp.role_info = gettemp.role_info + "," + name;
      }
    }
    else {
      // 取消选中，则删除当前的id和template name
      var temp_name = gettemp.role_info.split(',')
      var words = gettemp.role_ids.split(',')
      for (var i = 0; i < words.length; i++) {

        if (words[i] == id) {
          words.splice(i, 1);
          temp_name.splice(i, 1);
        }
      }
      gettemp.role_ids = words.join(",")
      gettemp.role_info = temp_name.join(",")
    }

    //console.log(gettemp.role_ids);
  }

}<|endoftext|>


<commit_before>angular.module( 'app' )
.controller( 'ApplicationCtrl', function( $scope, UserSvc, jwtHelper ) {
    $scope.$on( 'login', function( _, user ) {
        $scope.currentUser = user
    })
    if (window.localStorage.token) {
        $scope.$emit( 'login', jwtHelper.decodeToken(window.localStorage.token) )
        UserSvc.getUser()
    }
    $scope.logout = function() {
        if ($scope.currentUser) {
            delete $scope.currentUser
            UserSvc.removeToken()
        }
    }
})<commit_msg>Use angular $window instead of window global<commit_after>angular.module( 'app' )
.controller( 'ApplicationCtrl', function( $scope, $window, UserSvc, jwtHelper ) {
    $scope.$on( 'login', function( _, user ) {
        $scope.currentUser = user
    })
    if ($window.localStorage.token) {
        $scope.$emit( 'login', jwtHelper.decodeToken($window.localStorage.token) )
        UserSvc.getUser()
    }
    $scope.logout = function() {
        if ($scope.currentUser) {
            delete $scope.currentUser
            UserSvc.removeToken()
        }
    }
})<|endoftext|>


<commit_before>/****************************************************************************
 Copyright (c) 2013-2014 Chukong Technologies Inc.

 http://www.cocos2d-x.org

 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 in the Software without restriction, including without limitation the rights
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the Software is
 furnished to do so, subject to the following conditions:

 The above copyright notice and this permission notice shall be included in
 all copies or substantial portions of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
 ****************************************************************************/

(function(load, baseParser){

    var cache = {};

    var Parser = baseParser.extend({

        getNodeJson: function(json){
            return json["Content"]["Content"]["Animation"];
        },

        parseNode: function(json, resourcePath, file){
            if(!json)
                return null;
            if(cache[file])
                return cache[file].clone();

            var self = this,
                action = new ccs.ActionTimeline();

            action.setDuration(json["Duration"]);
            action.setTimeSpeed(json["Speed"] || 1);
            //The process of analysis
            var timelines = json["Timelines"];
            timelines.forEach(function(timeline){
                var parser = self.parsers[timeline["Property"]];
                var frame;
                if(parser)
                    frame = parser.call(self, timeline, resourcePath);
                else
                    cc.log("parser is not exists : %s", timeline["Property"]);
                if(frame)
                    action.addTimeline(frame);
            });

            cache[file] = action;
            cache[file].retain();
            return action.clone();
        }

    });
    var parser = new Parser();

    var frameList = [
        {
            name: "Position",
            handle: function(options){
                var frame = new ccs.PositionFrame();
                var x = options["X"];
                var y = options["Y"];
                frame.setPosition(cc.p(x,y));
                return frame;
            }
        },
        {
            name: "VisibleForFrame",
            handle: function(options){
                var frame = new ccs.VisibleFrame();
                var visible = options["Value"];
                frame.setVisible(visible);
                return frame;
            }
        },
        {
            name: "Scale",
            handle: function(options){
                var frame = new ccs.ScaleFrame();
                var scalex = options["X"];
                var scaley = options["Y"];
                frame.setScaleX(scalex);
                frame.setScaleY(scaley);
                return frame;
            }
        },
        {
            name: "Rotation",
            handle: function(options){
                var frame = new ccs.RotationFrame();
                var rotation = options["Rotation"];
                frame.setRotation(rotation);
                return frame;
            }
        },
        {
            name: "Skew",
            handle: function(options){
                var frame = new ccs.SkewFrame();
                var skewx = options["X"];
                var skewy = options["Y"];
                frame.setSkewX(skewx);
                frame.setSkewY(skewy);
                return frame;
            }
        },
        {
            name: "RotationSkew",
            handle: function(options){
                var frame = new ccs.RotationSkewFrame();
                var skewx = options["X"];
                var skewy = options["Y"];
                frame.setSkewX(skewx);
                frame.setSkewY(skewy);
                return frame;
            }
        },
        {
            name: "Anchor",
            handle: function(options){
                var frame = new ccs.AnchorPointFrame();
                var anchorx = options["X"];
                var anchory = options["Y"];
                frame.setAnchorPoint(cc.p(anchorx, anchory));
                return frame;
            }
        },
        {
            name: "InnerAction",
            handle: function(options){
                var frame = new ccs.InnerActionFrame();
                var type = options["InnerActionType"];
                var startFrame = options["StartFrame"];
                frame.setInnerActionType(type);
                frame.setStartFrameIndex(startFrame);
                return frame;
            }
        },
        {
            name: "CColor",
            handle: function(options){
                var frame = new ccs.ColorFrame();
                var color = options["Color"];
                if(!color) color = {};
                color["R"] = color["R"] || 255;
                color["G"] = color["G"] || 255;
                color["B"] = color["B"] || 255;
                frame.setColor(cc.color(color["R"], color["G"], color["B"]));
                return frame;
            }
        },
        {
            name: "Alpha",
            handle: function(options){
                var frame = new ccs.AlphaFrame();
                var alpha = options["Value"];
                frame.setAlpha(alpha);
                return frame;
            }
        },
        {
            name: "FileData",
            handle: function(options, resourcePath){
                var frame = new ccs.TextureFrame();
                var texture = options["TextureFile"];
                if(texture != null) {
                    var path = texture["Path"];
                    var spriteFrame = cc.spriteFrameCache.getSpriteFrame(path);
                    if(spriteFrame == null){
                        path = resourcePath + path;
                    }
                    frame.setTextureName(path);
                }
                return frame;
            }
        },
        {
            name: "FrameEvent",
            handle: function(options){
                var frame = new ccs.EventFrame();
                var evnt = options["Value"];
                if(evnt != null)
                    frame.setEvent(evnt);
                return frame;
            }
        },
        {
            name: "ZOrder",
            handle: function(options){
                var frame = new ccs.ZOrderFrame();
                var zorder = options["Value"];
                frame.setZOrder(zorder);
                return frame;
            }
        },
        {
            name: "ActionValue",
            handle: function (options) {

                var frame = new ccs.InnerActionFrame();
                var innerActionType = options["InnerActionType"];

                var currentAnimationFrame = options["CurrentAniamtionName"];

                var singleFrameIndex = options["SingleFrameIndex"];

                frame.setInnerActionType(ccs.InnerActionType[innerActionType]);
                frame.setSingleFrameIndex(singleFrameIndex);

                frame.setEnterWithName(true);
                frame.setAnimationName(currentAnimationFrame);

                return frame;
            }
        }
    ];

    frameList.forEach(function(item){
        parser.registerParser(item.name, function(options, resourcePath){
            var timeline = new ccs.Timeline();
            timeline.setActionTag(options["ActionTag"]);

            var frames = options["Frames"];
            if(frames && frames.length){
                frames.forEach(function(frameData){
                    var frame = item.handle(frameData, resourcePath);
                    frame.setFrameIndex(frameData["FrameIndex"]);
                    var tween = frameData["Tween"] != null ? frameData["Tween"] : true;
                    frame.setTween(tween);
                    timeline.addFrame(frame);
                });
            }
            return timeline;
        });
    });

    load.registerParser("action", "2.*", parser);

})(ccs._load, ccs._parser);
<commit_msg>studio parser‘s issue<commit_after>/****************************************************************************
 Copyright (c) 2013-2014 Chukong Technologies Inc.

 http://www.cocos2d-x.org

 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 in the Software without restriction, including without limitation the rights
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the Software is
 furnished to do so, subject to the following conditions:

 The above copyright notice and this permission notice shall be included in
 all copies or substantial portions of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
 ****************************************************************************/

(function(load, baseParser){

    var cache = {};

    var Parser = baseParser.extend({

        getNodeJson: function(json){
            return json["Content"]["Content"]["Animation"];
        },

        parseNode: function(json, resourcePath, file){
            if(!json)
                return null;
            if(cache[file])
                return cache[file].clone();

            var self = this,
                action = new ccs.ActionTimeline();

            action.setDuration(json["Duration"]);
            action.setTimeSpeed(json["Speed"] || 1);
            //The process of analysis
            var timelines = json["Timelines"];
            timelines.forEach(function(timeline){
                var parser = self.parsers[timeline["Property"]];
                var frame;
                if(parser)
                    frame = parser.call(self, timeline, resourcePath);
                else
                    cc.log("parser is not exists : %s", timeline["Property"]);
                if(frame)
                    action.addTimeline(frame);
            });

            cache[file] = action;
            cache[file].retain();
            return action.clone();
        },

        deferred: function(json, resourcePath, action, file){
            if(cc.sys.isNative){
                var animationlist = json["Content"]["Content"]["AnimationList"];
                var length = animationlist ? animationlist.length : 0;
                for (var i = 0; i < length; i++){
                    var animationdata = animationlist[i];
                    var info = { name: null, startIndex: null, endIndex: null };
                    info.name = animationdata["Name"];
                    info.startIndex = animationdata["StartIndex"];
                    info.endIndex = animationdata["EndIndex"];
                    action.addAnimationInfo(info);
                }
            }
        }

    });
    var parser = new Parser();

    var frameList = [
        {
            name: "Position",
            handle: function(options){
                var frame = new ccs.PositionFrame();
                var x = options["X"];
                var y = options["Y"];
                frame.setPosition(cc.p(x,y));
                return frame;
            }
        },
        {
            name: "VisibleForFrame",
            handle: function(options){
                var frame = new ccs.VisibleFrame();
                var visible = options["Value"];
                frame.setVisible(visible);
                return frame;
            }
        },
        {
            name: "Scale",
            handle: function(options){
                var frame = new ccs.ScaleFrame();
                var scalex = options["X"];
                var scaley = options["Y"];
                frame.setScaleX(scalex);
                frame.setScaleY(scaley);
                return frame;
            }
        },
        {
            name: "Rotation",
            handle: function(options){
                var frame = new ccs.RotationFrame();
                var rotation = options["Rotation"];
                frame.setRotation(rotation);
                return frame;
            }
        },
        {
            name: "Skew",
            handle: function(options){
                var frame = new ccs.SkewFrame();
                var skewx = options["X"];
                var skewy = options["Y"];
                frame.setSkewX(skewx);
                frame.setSkewY(skewy);
                return frame;
            }
        },
        {
            name: "RotationSkew",
            handle: function(options){
                var frame = new ccs.RotationSkewFrame();
                var skewx = options["X"];
                var skewy = options["Y"];
                frame.setSkewX(skewx);
                frame.setSkewY(skewy);
                return frame;
            }
        },
        {
            name: "Anchor",
            handle: function(options){
                var frame = new ccs.AnchorPointFrame();
                var anchorx = options["X"];
                var anchory = options["Y"];
                frame.setAnchorPoint(cc.p(anchorx, anchory));
                return frame;
            }
        },
        {
            name: "InnerAction",
            handle: function(options){
                var frame = new ccs.InnerActionFrame();
                var type = options["InnerActionType"];
                var startFrame = options["StartFrame"];
                frame.setInnerActionType(type);
                frame.setStartFrameIndex(startFrame);
                return frame;
            }
        },
        {
            name: "CColor",
            handle: function(options){
                var frame = new ccs.ColorFrame();
                var color = options["Color"];
                if(!color) color = {};
                color["R"] = color["R"] || 255;
                color["G"] = color["G"] || 255;
                color["B"] = color["B"] || 255;
                frame.setColor(cc.color(color["R"], color["G"], color["B"]));
                return frame;
            }
        },
        {
            name: "Alpha",
            handle: function(options){
                var frame = new ccs.AlphaFrame();
                var alpha = options["Value"];
                frame.setAlpha(alpha);
                return frame;
            }
        },
        {
            name: "FileData",
            handle: function(options, resourcePath){
                var frame = new ccs.TextureFrame();
                var texture = options["TextureFile"];
                if(texture != null) {
                    var path = texture["Path"];
                    var spriteFrame = cc.spriteFrameCache.getSpriteFrame(path);
                    if(spriteFrame == null){
                        path = resourcePath + path;
                    }
                    frame.setTextureName(path);
                }
                return frame;
            }
        },
        {
            name: "FrameEvent",
            handle: function(options){
                var frame = new ccs.EventFrame();
                var evnt = options["Value"];
                if(evnt != null)
                    frame.setEvent(evnt);
                return frame;
            }
        },
        {
            name: "ZOrder",
            handle: function(options){
                var frame = new ccs.ZOrderFrame();
                var zorder = options["Value"];
                frame.setZOrder(zorder);
                return frame;
            }
        },
        {
            name: "ActionValue",
            handle: function (options) {

                var frame = new ccs.InnerActionFrame();
                var innerActionType = options["InnerActionType"];

                var currentAnimationFrame = options["CurrentAniamtionName"];

                var singleFrameIndex = options["SingleFrameIndex"];

                frame.setInnerActionType(ccs.InnerActionType[innerActionType]);
                frame.setSingleFrameIndex(singleFrameIndex);

                frame.setEnterWithName(true);
                frame.setAnimationName(currentAnimationFrame);

                return frame;
            }
        }
    ];

    frameList.forEach(function(item){
        parser.registerParser(item.name, function(options, resourcePath){
            var timeline = new ccs.Timeline();
            timeline.setActionTag(options["ActionTag"]);

            var frames = options["Frames"];
            if(frames && frames.length){
                frames.forEach(function(frameData){
                    var frame = item.handle(frameData, resourcePath);
                    frame.setFrameIndex(frameData["FrameIndex"]);
                    var tween = frameData["Tween"] != null ? frameData["Tween"] : true;
                    frame.setTween(tween);
                    timeline.addFrame(frame);
                });
            }
            return timeline;
        });
    });

    load.registerParser("action", "2.*", parser);

})(ccs._load, ccs._parser);
<|endoftext|>


<commit_before>const debug = require('ghost-ignition').debug('services:routing:bootstrap');
const _ = require('lodash');
const settingsService = require('../settings');
const StaticRoutesRouter = require('./StaticRoutesRouter');
const StaticPagesRouter = require('./StaticPagesRouter');
const CollectionRouter = require('./CollectionRouter');
const TaxonomyRouter = require('./TaxonomyRouter');
const PreviewRouter = require('./PreviewRouter');
const ParentRouter = require('./ParentRouter');

const registry = require('./registry');

/**
 * Create a set of default and dynamic routers defined in the routing yaml.
 *
 * @TODO:
 *   - is the PreviewRouter an app?
 */
module.exports = function bootstrap() {
    debug('bootstrap');

    registry.resetAllRouters();
    registry.resetAllRoutes();

    const siteRouter = new ParentRouter('SiteRouter');
    const previewRouter = new PreviewRouter();

    siteRouter.mountRouter(previewRouter.router());

    registry.setRouter('siteRouter', siteRouter);
    registry.setRouter('previewRouter', previewRouter);

    const dynamicRoutes = settingsService.get('routes');

    _.each(dynamicRoutes.taxonomies, (value, key) => {
        const taxonomyRouter = new TaxonomyRouter(key, value);
        siteRouter.mountRouter(taxonomyRouter.router());

        registry.setRouter(taxonomyRouter.identifier, taxonomyRouter);
    });

    _.each(dynamicRoutes.routes, (value, key) => {
        const staticRoutesRouter = new StaticRoutesRouter(key, value);
        siteRouter.mountRouter(staticRoutesRouter.router());

        registry.setRouter(staticRoutesRouter.identifier, staticRoutesRouter);
    });

    _.each(dynamicRoutes.collections, (value, key) => {
        const collectionRouter = new CollectionRouter(key, value);
        siteRouter.mountRouter(collectionRouter.router());
        registry.setRouter(collectionRouter.identifier, collectionRouter);
    });

    const staticPagesRouter = new StaticPagesRouter();
    siteRouter.mountRouter(staticPagesRouter.router());

    registry.setRouter('staticPagesRouter', staticPagesRouter);

    const appRouter = new ParentRouter('AppsRouter');
    siteRouter.mountRouter(appRouter.router());

    registry.setRouter('appRouter', appRouter);

    debug('Routes:', registry.getAllRoutes());
    return siteRouter.router();
};
<commit_msg>Dynamic Routing Beta: Reordered router hierarchy<commit_after>const debug = require('ghost-ignition').debug('services:routing:bootstrap');
const _ = require('lodash');
const settingsService = require('../settings');
const StaticRoutesRouter = require('./StaticRoutesRouter');
const StaticPagesRouter = require('./StaticPagesRouter');
const CollectionRouter = require('./CollectionRouter');
const TaxonomyRouter = require('./TaxonomyRouter');
const PreviewRouter = require('./PreviewRouter');
const ParentRouter = require('./ParentRouter');

const registry = require('./registry');

/**
 * Create a set of default and dynamic routers defined in the routing yaml.
 *
 * @TODO:
 *   - is the PreviewRouter an app?
 */
module.exports = function bootstrap() {
    debug('bootstrap');

    registry.resetAllRouters();
    registry.resetAllRoutes();

    const siteRouter = new ParentRouter('SiteRouter');
    const previewRouter = new PreviewRouter();

    siteRouter.mountRouter(previewRouter.router());

    registry.setRouter('siteRouter', siteRouter);
    registry.setRouter('previewRouter', previewRouter);

    const dynamicRoutes = settingsService.get('routes');

    _.each(dynamicRoutes.routes, (value, key) => {
        const staticRoutesRouter = new StaticRoutesRouter(key, value);
        siteRouter.mountRouter(staticRoutesRouter.router());

        registry.setRouter(staticRoutesRouter.identifier, staticRoutesRouter);
    });

    _.each(dynamicRoutes.taxonomies, (value, key) => {
        const taxonomyRouter = new TaxonomyRouter(key, value);
        siteRouter.mountRouter(taxonomyRouter.router());

        registry.setRouter(taxonomyRouter.identifier, taxonomyRouter);
    });

    _.each(dynamicRoutes.collections, (value, key) => {
        const collectionRouter = new CollectionRouter(key, value);
        siteRouter.mountRouter(collectionRouter.router());
        registry.setRouter(collectionRouter.identifier, collectionRouter);
    });

    const staticPagesRouter = new StaticPagesRouter();
    siteRouter.mountRouter(staticPagesRouter.router());

    registry.setRouter('staticPagesRouter', staticPagesRouter);

    const appRouter = new ParentRouter('AppsRouter');
    siteRouter.mountRouter(appRouter.router());

    registry.setRouter('appRouter', appRouter);

    debug('Routes:', registry.getAllRoutes());
    return siteRouter.router();
};
<|endoftext|>


<commit_before>'use strict';

// ## NOTE ################################################################ //
//                                                                          //
// History.js works poorly with URLs containing hashes:                     //
//                                                                          //
//    https://github.com/browserstate/history.js/issues/111                 //
//    https://github.com/browserstate/history.js/issues/173                 //
//                                                                          //
// So upon clicks on `/foo#bar` we treat URL and push it to the state as    //
// `/foo` and saving `bar` in the state data, so we could scroll to desired //
// element upon statechange                                                 //
//                                                                          //
// ######################################################################## //

var _ = require('lodash');

var History = window.History; // History.js

// Takes a params hash (can be nested) and tries to parse each string-value as
// number or boolean. Returns a new hash with parsed values.
//
function castParamTypes(inputValue) {
  var parsedValue;

  if (_.isArray(inputValue)) {
    return _.map(inputValue, castParamTypes);

  } else if (_.isObject(inputValue)) {
    parsedValue = {};

    _.forEach(inputValue, function (value, key) {
      parsedValue[key] = castParamTypes(value);
    });

    return parsedValue;

  } else if ('true' === inputValue) {
    return true;

  } else if ('false' === inputValue) {
    return false;

  } else if (/^[0-9\.\-]+$/.test(inputValue)) {
    parsedValue = Number(inputValue);
    return String(parsedValue) === inputValue ? parsedValue : inputValue;

  } else {
    return inputValue;
  }
}

// Returns a normalized URL:
//
//  http://example.com/foo.html  => http://example.com/foo.html
//  /foo.html                    => http://example.com/foo.html
//  //example.com/foo.html       => http://example.com/foo.html
//
function normalizeURL(url) {
  var a = document.createElement('a');
  a.href = url;
  return a.href.toString();
}

// Default renderer for `navigate.to` event.
// Used to render content when user clicks a link.
//
function renderNewContent(data, callback) {
  var content = $(N.runtime.render(data.view, data.locals, {
    apiPath: data.apiPath
  })).hide();

  $('#content').fadeOut('fast', function () {
    var offset = data.anchor ? $(data.anchor).offset() : null;

    $(this).replaceWith(content);
    content.fadeIn('fast');

    // To scroll window:
    // - WebKit-based browsers and the quirks mode use `body` element.
    // - Other browsers use `html` element.
    $('html:not(:animated), body:not(:animated)').animate({
      scrollTop:  offset ? offset.top  : 0
    , scrollLeft: offset ? offset.left : 0
    }, 300);

    callback();
  });
}

// Used to render content when user presses Back/Forward buttons.
//
function renderFromHistory(data, callback) {
  var content = $(N.runtime.render(data.view, data.locals, {
    apiPath: data.apiPath
  })).hide();

  $('#content').fadeOut('fast', function () {
    $(this).replaceWith(content);
    content.fadeIn('fast');
    callback();
  });
}

// Reference to a function to be used on next fire of history 'statechange' event
// to perform content injection/replacement.
//
// NOTE: The event handler *always* resets this variable to `renderFromHistory`
// after each call.
var __renderCallback__ = renderFromHistory;

// Reference to a function to be used by history 'statechange' handler after
// content rendering is done.
//
// NOTE: The event handler *always* resets this variable to null after each call.
var __completeCallback__ = null;

// API path of current page. Updated via `navigate.done` event.
var __currentApiPath__ = null;

// Performs RPC navigation to the specified page. Allowed options:
//
//    options.href
//    options.apiPath
//    options.params
//    options.render       - optional function; default is `renderNewContent`
//    options.replaceState - `true` to use `History.replaceState` instead of
//                           `History.pushState`
//
// `href` and `apiPath` parameters are calculated from each other.
// So they are mutually exclusive.
//
N.wire.on('navigate.to', function navigate_to(options, callback) {
  var match, href, anchor, apiPath, params, errorReport;

  if ('string' === typeof options) {
    options = { href: options };
  }

  if (options.href) {
    match = _.find(N.runtime.router.matchAll(options.href), function (match) {
      return _.has(match.meta.methods, 'get');
    });

    // It's an external link or 404 error if route is not matched. So perform
    // regular page requesting via HTTP.
    if (!match) {
      window.location = normalizeURL(options.href);
      callback();
      return;
    }

    match.params = castParamTypes(match.params);

    apiPath = match.meta.methods.get;
    params  = match.params || {};
    href    = options.href.split('#')[0];
    anchor  = options.href.split('#')[1] || '';

  } else if (options.apiPath) {
    apiPath = options.apiPath;
    params  = options.params || {};
    href    = N.runtime.router.linkTo(apiPath, params);
    anchor  = options.anchor || '';

    if (!href) {
      errorReport = 'Invalid parameters passed to `navigate.to` event: ' +
                    JSON.stringify(options);

      window.alert(errorReport);
      callback(new Error(errorReport));
      return;
    }

  } else {
    errorReport = 'Not enough parameters for `navigate.to` event. ' +
                  'Need `href` or `apiPath` at least: ' +
                  JSON.stringify(options);

    window.alert(errorReport);
    callback(new Error(errorReport));
    return;
  }

  // History.JS does not plays well with full URLs but without protocols:
  //
  //  http://example.com/foo.html  -- OK
  //  /foo.html                    -- OK
  //  //example.com/foo.html       -- becomes /example.com/foo.html
  //
  // So we normalize URL to be full one (with protocol, host, etc.)
  href = normalizeURL(href);

  // Add anchor hash-prefix if not exists.
  if (anchor && '#' !== anchor.charAt(0)) {
    anchor = '#' + anchor;
  }

  // Stop here if base URL (all except anchor) haven't changed.
  if (href === (location.protocol + '//' + location.host + location.pathname)) {
    if (anchor !== location.hash) {
      location.hash = anchor;
    }

    callback();
    return;
  }

  // Fallback for old browsers.
  if (!History.enabled) {
    window.location = href + anchor;
    callback();
    return;
  }

  // History is enabled - try RPC navigation.
  N.io.rpc(apiPath, params, function (err, response) {
    if (err && N.io.REDIRECT === err.code) {
      var redirectUrl = document.createElement('a');

      // Tricky way to parse URL.
      redirectUrl.href = err.head.Location;

      // Note, that we try to keep anchor, if exists.
      // That's important for moved threads and last pages redirects.
      redirectUrl.hash = anchor || window.location.hash;

      // If protocol is changed, we must completely reload the page to keep
      // Same-origin policy for RPC.
      // - port check not required, because port depends on protocol.
      // - domain check not required, because RPC is available on all domains
      //   (it uses relative path)
      if (redirectUrl.protocol !== location.protocol) {
        window.location = redirectUrl.href;
        callback();
      } else {
        N.wire.emit('navigate.to', {
          href:    redirectUrl.href
        , render:  options.render
        , history: options.history
        }, callback);
      }
      return;
    }

    if (err) {
      // Can't deal via RPC - try HTTP. This might be:
      //
      // - Either a generic error, e.g. authorization / bad params / fuckup
      //   so we redirect user to show him an error page.
      //
      // - Version mismatch, so we call action by HTTP to update client.
      window.location = href + anchor;
      callback();
      return;
    }

    /*
    if (response.layout !== N.runtime.layout) {
      // Layout was changed - perform normal page loading.
      //
      // TODO: Prevent double page requesting. The server should not perform
      // database queries on RPC when the client is not intending to use the
      // response data. Like in this situation.
      window.location = href + anchor;
      callback();
      return;
    }
    */

    N.loader.loadAssets((response.view || apiPath).split('.')[0], function () {
      var state = {
        apiPath: apiPath
      , anchor:  anchor
      , view:    response.view   || apiPath
      //, layout:  response.layout || null
      , locals:  response.data   || {}
      };

      // Set one-use callbacks for history 'statechange' handler.
      // The handler will reset these to defaults (`renderFromHistory` and null).
      __renderCallback__   = options.render || renderNewContent;
      __completeCallback__ = callback;

      if (options.replaceState) {
        History.replaceState(state, response.data.head.title, href);
      } else {
        History.pushState(state, response.data.head.title, href);
      }
    });
  });
});

//
// Bind History's statechange handler. It fires when:
//
// - User presses `Back` or `Forward` button in his browser.
// - User clicks a link.
// - User clicks "More threads/posts/etc" button.
//

if (History.enabled) {
  History.Adapter.bind(window, 'statechange', function () {
    var state      = History.getState()
      , exitData   = { apiPath: __currentApiPath__, url: state.url }
      , exitEvents = ['navigate.exit:' + __currentApiPath__, 'navigate.exit']
      , doneData   = { apiPath: state.data.apiPath, url: state.url }
      , doneEvents = ['navigate.done', 'navigate.done:' + state.data.apiPath]
      , render     = __renderCallback__
      , complete   = __completeCallback__;

    // Reset callbacks to defaults. It's needed to ensure using right renderer
    // on regular history state changes - when user clicks back/forward buttons
    // in his browser.
    __renderCallback__   = renderFromHistory;
    __completeCallback__ = null;

    // We have no state data when it's an initial state, so we schedule
    // retrieval of data by it's URL and triggering this event once
    // again (via History.replaceState).
    if (!state.data || History.isEmptyObject(state.data)) {
      N.wire.emit('navigate.to', {
        href:    state.url
      , history: History.replaceState
      });
      return;
    }

    N.wire.emit(exitEvents, exitData, function (err) {
      if (err) {
        N.logger.error('%s', err);
      }

      // Clear old raw response data. It's collected by view templates.
      N.runtime.page_data = {};

      render(state.data, function () {
        N.wire.emit(doneEvents, doneData, function (err) {
          if (err) {
            N.logger.error('%s', err);
          }

          if (complete) {
            complete();
          }
        });
      });
    });
  });
}

//
// __currentApiPath__ updater.
//

N.wire.on('navigate.done', { priority: -999 }, function (data) {
  __currentApiPath__ = data.apiPath;
});

//
// Bind global a.click handler.
//
// NOTE: This handler must have *the lowest* priority. So:
//
// - It must be binded to `document`, since root DOM node has the lowest
//   priotity on event bubbling.
//
// - External libraries like Bootstrap must be placed *before* Nodeca client
//   code to ensure right order of handlers.
//

N.wire.once('navigate.done', { priority: 999 }, function () {
  $(document).on('click', 'a', function (event) {
    var $this = $(this);

    // TODO: Do we really need this? Commented out until the decision.
    // Always focus the link or blur the current focus at least since browsers
    // may not allow to focus certain elements.
    //$(document.activeElement).blur();
    //$this.focus();

    if ($this.attr('target') || event.isDefaultPrevented()) {
      // skip links that have `target` attribute specified
      // and clicks that were already handled
      return;
    }

    // Continue as normal for cmd clicks etc
    if (2 === event.which || event.metaKey) {
      return;
    }

    N.wire.emit('navigate.to', $this.attr('href'), function (err) {
      if (err) {
        N.logger.error('%s', err);
      }
    });

    event.preventDefault();
  });
});
<commit_msg>Sync admin's navigate.js with common's one.<commit_after>'use strict';

// ## NOTE ################################################################ //
//                                                                          //
// History.js works poorly with URLs containing hashes:                     //
//                                                                          //
//    https://github.com/browserstate/history.js/issues/111                 //
//    https://github.com/browserstate/history.js/issues/173                 //
//                                                                          //
// So upon clicks on `/foo#bar` we treat URL and push it to the state as    //
// `/foo` and saving `bar` in the state data, so we could scroll to desired //
// element upon statechange                                                 //
//                                                                          //
// ######################################################################## //

var _ = require('lodash');

var History = window.History; // History.js

// Takes a params hash (can be nested) and tries to parse each string-value as
// number or boolean. Returns a new hash with parsed values.
//
function castParamTypes(inputValue) {
  var parsedValue;

  if (_.isArray(inputValue)) {
    return _.map(inputValue, castParamTypes);

  } else if (_.isObject(inputValue)) {
    parsedValue = {};

    _.forEach(inputValue, function (value, key) {
      parsedValue[key] = castParamTypes(value);
    });

    return parsedValue;

  } else if ('true' === inputValue) {
    return true;

  } else if ('false' === inputValue) {
    return false;

  } else if (/^[0-9\.\-]+$/.test(inputValue)) {
    parsedValue = Number(inputValue);
    return String(parsedValue) === inputValue ? parsedValue : inputValue;

  } else {
    return inputValue;
  }
}

// Returns a normalized URL:
//
//  http://example.com/foo.html  => http://example.com/foo.html
//  /foo.html                    => http://example.com/foo.html
//  //example.com/foo.html       => http://example.com/foo.html
//
// NOTE: History.JS does not plays well with full URLs but without protocols.
//
function normalizeURL(url) {
  var a = document.createElement('a');
  a.href = url;
  return a.href.toString();
}

// Default renderer for `navigate.to` event.
// Used to render content when user clicks a link.
//
function renderNewContent(data, callback) {
  var content = $(N.runtime.render(data.view, data.locals, {
    apiPath: data.apiPath
  })).hide();

  $('#content').fadeOut('fast', function () {
    var offset = data.anchor ? $(data.anchor).offset() : null;

    $(this).replaceWith(content);
    content.fadeIn('fast');

    // To scroll window:
    // - WebKit-based browsers and the quirks mode use `body` element.
    // - Other browsers use `html` element.
    $('html:not(:animated), body:not(:animated)').animate({
      scrollTop:  offset ? offset.top  : 0
    , scrollLeft: offset ? offset.left : 0
    }, 300);

    callback();
  });
}

// Used to render content when user presses Back/Forward buttons.
//
function renderFromHistory(data, callback) {
  var content = $(N.runtime.render(data.view, data.locals, {
    apiPath: data.apiPath
  })).hide();

  $('#content').fadeOut('fast', function () {
    $(this).replaceWith(content);
    content.fadeIn('fast');
    callback();
  });
}

// Ignore next History 'statechange' event if true.
//
// NOTE: The event handler *always* resets this variable to false after each call.
var __dryHistoryChange__ = false;

// Reference to a function to be used on next fire of history 'statechange' event
// to perform content injection/replacement.
//
// NOTE: The event handler *always* resets this variable to `renderFromHistory`
// after each call.
var __renderCallback__ = renderFromHistory;

// Reference to a function to be used by history 'statechange' handler after
// content rendering is done.
//
// NOTE: The event handler *always* resets this variable to null after each call.
var __completeCallback__ = null;

// API path of current page. Updated via `navigate.done` event.
var __currentApiPath__ = null;

// Performs RPC navigation to the specified page. Allowed options:
//
//    options.href
//    options.apiPath
//    options.params
//
// `href` and `apiPath` parameters are calculated from each other.
// So they are mutually exclusive.
//
N.wire.on('navigate.to', function navigate_to(options, callback) {
  var match, href, anchor, apiPath, params, errorReport;

  if ('string' === typeof options) {
    options = { href: options };
  }

  if (options.href) {
    href   = normalizeURL(options.href).split('#')[0];
    anchor = normalizeURL(options.href).slice(href.length) || '';

    match = _.find(N.runtime.router.matchAll(href), function (match) {
      return _.has(match.meta.methods, 'get');
    });

    // It's an external link or 404 error if route is not matched. So perform
    // regular page requesting via HTTP.
    if (!match) {
      window.location = href + anchor;
      callback();
      return;
    }

    apiPath = match.meta.methods.get;
    params = castParamTypes(match.params || {});

  } else if (options.apiPath) {
    apiPath = options.apiPath;
    params  = options.params || {};
    href    = normalizeURL(N.runtime.router.linkTo(apiPath, params));
    anchor  = options.anchor || '';

    if (!href) {
      errorReport = 'Invalid parameters passed to `navigate.to` event: ' +
                    JSON.stringify(options);

      window.alert(errorReport);
      callback(new Error(errorReport));
      return;
    }

  } else {
    errorReport = 'Not enough parameters for `navigate.to` event. ' +
                  'Need `href` or `apiPath` at least: ' +
                  JSON.stringify(options);

    window.alert(errorReport);
    callback(new Error(errorReport));
    return;
  }

  // Add anchor hash-prefix if not exists.
  if (anchor && '#' !== anchor.charAt(0)) {
    anchor = '#' + anchor;
  }

  // Stop here if base URL (all except anchor) haven't changed.
  if (href === (location.protocol + '//' + location.host + location.pathname)) {

    // Update anchor if it's changed.
    if (location.hash !== anchor) {
      location.hash = anchor;
    }

    callback();
    return;
  }

  // Fallback for old browsers.
  if (!History.enabled) {
    window.location = href + anchor;
    callback();
    return;
  }

  // History is enabled - try RPC navigation.
  N.io.rpc(apiPath, params, function (err, response) {
    if (err && N.io.REDIRECT === err.code) {
      var redirectUrl = document.createElement('a');

      // Tricky way to parse URL.
      redirectUrl.href = err.head.Location;

      // Note, that we try to keep anchor, if exists.
      // That's important for moved threads and last pages redirects.
      redirectUrl.hash = anchor || window.location.hash;

      // If protocol is changed, we must completely reload the page to keep
      // Same-origin policy for RPC.
      // - port check not required, because port depends on protocol.
      // - domain check not required, because RPC is available on all domains
      //   (it uses relative path)
      if (redirectUrl.protocol !== location.protocol) {
        window.location = redirectUrl.href;
        callback();
      } else {
        N.wire.emit('navigate.to', { href: redirectUrl.href }, callback);
      }
      return;
    }

    if (err) {
      // Can't deal via RPC - try HTTP. This might be:
      //
      // - Either a generic error, e.g. authorization / bad params / fuckup
      //   so we redirect user to show him an error page.
      //
      // - Version mismatch, so we call action by HTTP to update client.
      window.location = href + anchor;
      callback();
      return;
    }

    /*
    if (response.layout !== N.runtime.layout) {
      // Layout was changed - perform normal page loading.
      //
      // TODO: Prevent double page requesting. The server should not perform
      // database queries on RPC when the client is not intending to use the
      // response data. Like in this situation.
      window.location = href + anchor;
      callback();
      return;
    }
    */

    N.loader.loadAssets((response.view || apiPath).split('.')[0], function () {
      var state = {
        apiPath: apiPath
      , anchor:  anchor
      , view:    response.view   || apiPath
    //, layout:  response.layout || null
      , locals:  response.data   || {}
      };

      // Set one-use callbacks for history 'statechange' handler.
      // The handler will reset these to defaults (`renderFromHistory` and null).
      __renderCallback__   = renderNewContent;
      __completeCallback__ = callback;

      History.pushState(state, response.data.head.title, href);
    });
  });
});

// Replace current History state without data fetching and rendering.
//
//   options.href  - full url of new history state. (required)
//   options.title - new page title. (required)
//   options.data  - data for history renderer; it will be used when user will
//                   return to this page using history navigation. (optional)
//
N.wire.on('navigate.replace', function navigate_replace(options, callback) {
  __dryHistoryChange__ = true;
  __completeCallback__ = callback;

  History.replaceState(options.data || {}, options.title, options.href);
});

//
// Bind History's statechange handler. It fires when:
//
// - User presses `Back` or `Forward` button in his browser.
// - User clicks a link.
// - User clicks "More threads/posts/etc" button.
//

if (History.enabled) {
  History.Adapter.bind(window, 'statechange', function () {
    var state    = History.getState()
      , render   = __renderCallback__
      , complete = __completeCallback__;

    // Dry history change - just reset the flag and callback parameters for next
    // history state change, and invoke complete callback.
    if (__dryHistoryChange__) {
      __dryHistoryChange__ = false;
      __renderCallback__   = renderFromHistory;
      __completeCallback__ = null;

      if (complete) {
        complete();
      }
      return;
    }

    // We have no state data for the initial page (received via HTTP responder).
    // So request that data via RPC and place into History.
    if (_.isEmpty(state.data)) {
      var match = _.find(N.runtime.router.matchAll(state.url), function (match) {
        return _.has(match.meta.methods, 'get');
      });

      // Can't match initial URL by some reason - just reload the page.
      // This is internal error. Must not happen on normal Nodeca workflow.
      if (!match) {
        window.location = state.url;
        return;
      }

      // Retrieve data.
      N.io.rpc(match.meta.methods.get, castParamTypes(match.params || {}), function (err, response) {
        var a, data, url;

        // Simple way to parse URL in browser.
        a = document.createElement('a');
        a.href = state.url;

        // Result state data.
        data = {
          apiPath: match.meta.methods.get
        , anchor:  a.hash
        , view:    response.view   || match.meta.methods.get
      //, layout:  response.layout || null
        , locals:  response.data   || {}
        };

        // State URL without anchor. (due to a problem in History.js; see above)
        url = a.protocol + '//' + a.host + a.pathname;

        // We terminate here, but History.replaceState will trigger 'statechange'
        // again, and we will continue with next part of code.
        History.replaceState(data, state.title, url);
      });
      return;
    }

    // Restore callbacks to defaults. It's needed to ensure using right renderer
    // on regular history state changes - when user clicks back/forward buttons
    // in his browser.
    __renderCallback__   = renderFromHistory;
    __completeCallback__ = null;

    var exitEventData = { apiPath: __currentApiPath__, url: state.url }
      , doneEventData = { apiPath: state.data.apiPath, url: state.url };

    // Invoke exit handlers.
    N.wire.emit(['navigate.exit:' + __currentApiPath__, 'navigate.exit'], exitEventData, function (err) {
      if (err) {
        N.logger.error('%s', err); // Log error, but not stop.
      }

      // Clear old raw response data. It's collected by view templates.
      N.runtime.page_data = {};

      render(state.data, function () {
        // Invoke done-handlers.
        N.wire.emit(['navigate.done', 'navigate.done:' + state.data.apiPath], doneEventData, function (err) {
          if (err) {
            N.logger.error('%s', err); // Log error, but not stop.
          }

          if (complete) {
            complete();
          }
        });
      });
    });
  });
}

//
// __currentApiPath__ updater.
//

N.wire.on('navigate.done', { priority: -999 }, function apipath_set(data) {
  __currentApiPath__ = data.apiPath;
});

//
// Bind global a.click handler.
//
// NOTE: This handler must have *the lowest* priority. So:
//
// - It must be binded to `document`, since root DOM node has the lowest
//   priotity on event bubbling.
//
// - External libraries like Bootstrap must be placed *before* Nodeca client
//   code to ensure right order of handlers.
//

N.wire.once('navigate.done', { priority: 999 }, function navigate_click_handler() {
  $(document).on('click', 'a', function (event) {
    var $this = $(this);

    if ($this.attr('target') || event.isDefaultPrevented()) {
      // skip links that have `target` attribute specified
      // and clicks that were already handled
      return;
    }

    // Continue as normal for cmd clicks etc
    if (2 === event.which || event.metaKey) {
      return;
    }

    if ('#' === $this.attr('href')) {
      // Prevent clicks on special "button"-links.
      event.preventDefault();
      return;
    }

    N.wire.emit('navigate.to', $this.attr('href'), function (err) {
      if (err) {
        N.logger.error('%s', err);
      }
    });

    event.preventDefault();
  });
});
<|endoftext|>