describe("bootstrap-tagsinput", function() {

  describe("events", function() {
    testTagsInput('<input type="text" />', function() {
      it("beforeItemAdd canceled", function() {
        this.$element.on('beforeItemAdd', function(event) {
          event.cancel = true;
        });
        this.$element.tagsinput('add', 'some');
        expect(this.$element.tagsinput('items').length).toBe(0);
      });
    });

    testTagsInput('<input type="text" value="1" />', function() {
      it("beforeItemRemove canceled", function() {
        this.$element.on('beforeItemRemove', function(event) {
          event.cancel = true;
        });
        this.$element.tagsinput('remove', '1');
        expect(this.$element.tagsinput('items').length).toBe(1);
      });
    });
  });
});

describe("bootstrap-tagsinput", function () {

  describe("events", function () {
    testTagsInput('<input type="text" />', function () {
      it("beforeItemAdd canceled", function () {
        this.$element.on('beforeItemAdd', function (event) {
          event.cancel = true;
        });
        this.$element.tagsinput('add', 'some');
        expect(this.$element.tagsinput('items').length).toBe(0);
      });
    });

    testTagsInput('<input type="text" value="1" />', function () {
      it("beforeItemRemove canceled", function () {
        this.$element.on('beforeItemRemove', function (event) {
          event.cancel = true;
        });
        this.$element.tagsinput('remove', '1');
        expect(this.$element.tagsinput('items').length).toBe(1);
      });
    });

    testTagsInput('<input type="text" value="1" />', { triggerChange: false }, function () {
      it("triggerChange 'true' test", function () {
        var triggerChange = false;
        this.$element.on('change', function (event) {
          triggerChange = true;
        });
        this.$element.tagsinput('remove', '1');
        this.$element.tagsinput('add', 'some');
        expect(triggerChange).toBe(false);
      });
    });

    testTagsInput('<input type="text" value="1" />' , function () {
      it("triggerChange 'false' test", function () {
        var triggerChange = false;
        this.$element.on('change', function (event) {
          triggerChange = true;
        });
        this.$element.tagsinput('remove', '1');
        this.$element.tagsinput('add', 'some');
        expect(triggerChange).toBe(true);
      });
    });
  });
});

Add possibility skip "change" event after "add", "remove", "removeAll" methods


/**
 * Loads a script from a specific URL. React Native cannot load a JS
 * file/resource/URL via a <script> HTML element, so the implementation
 * fetches the specified <tt>url</tt> as plain text using {@link fetch()} and
 * then evaluates the fetched string as JavaScript code (using {@link eval()}).
 *
 * @param {string} url - The absolute URL from which the script is to be
 * (down)loaded.
 * @returns {void}
 */
export function loadScript(url) {
    return (
        fetch(url, { method: 'GET' })
            .then(response => {
                switch (response.status) {
                case 200:
                    return response.responseText || response.text();

                default:
                    throw response.statusText;
                }
            })
            .then(responseText => {
                eval.call(window, responseText); // eslint-disable-line no-eval
            }));
}

/**
 * Loads a script from a specific URL. React Native cannot load a JS
 * file/resource/URL via a <script> HTML element, so the implementation
 * fetches the specified <tt>url</tt> as plain text using {@link fetch()} and
 * then evaluates the fetched string as JavaScript code (using {@link eval()}).
 *
 * @param {string} url - The absolute URL from which the script is to be
 * (down)loaded.
 * @returns {void}
 */
export function loadScript(url) {
    return new Promise((resolve, reject) => {
        // XXX The implementation of fetch on Android will throw an Exception on
        // the Java side which will break the app if the URL is invalid (which
        // the implementation of fetch on Android calls 'unexpected url'). In
        // order to try to prevent the breakage of the app, try to fail on an
        // invalid URL as soon as possible.
        const { hostname, pathname, protocol } = new URL(url);

        // XXX The standard URL implementation should throw an Error if the
        // specified URL is relative. Unfortunately, the polyfill used on
        // react-native does not.
        if (!hostname || !pathname || !protocol) {
            reject(`unexpected url: ${url}`);

            return;
        }

        fetch(url, { method: 'GET' })
            .then(response => {
                switch (response.status) {
                case 200:
                    return response.responseText || response.text();

                default:
                    throw response.statusText;
                }
            })
            .then(responseText => {
                eval.call(window, responseText); // eslint-disable-line no-eval
            })
            .then(resolve, reject);
    });
}

Work around fetch-induced app breakage

[Android] Work around fetch-induced app breakage


(function(require) {
    require.config({
        baseUrl: "../",
        paths: {
            "jquery": "base1/jquery"
        }
    });
})(require);

(function(require) {
    require.config({
        baseUrl: "../",
        waitSeconds: 30,
        paths: {
            "jquery": "base1/jquery"
        }
    });
})(require);

base1: Increase the number of seconds before giving up on loading a script

The default is 7 seconds, which is causing timeouts on slow networks

Closes #2488
Reviewed-by: Stef Walter <8a7fbf0f15d3ad982af54ef28f5c117d2494b76c@redhat.com>


'use strict';

var gulp = require('gulp');
var runSequence = require('run-sequence');
var browserSync = require('browser-sync');

gulp.task('watch', ['browser-sync', 'html', 'sass', 'js'], function() {
  gulp.watch('./bundle.json', ['sanji-window-compiler']);
  gulp.watch('./src/window/*.scss', function() {
    runSequence('sass', function() {
      browserSync.reload({stream: true});
    });
  });
  gulp.watch('./src/window/*.js', function(event) {
    runSequence('js', 'bs-reload');
  });
  gulp.watch('./src/window/*.html', function() {
    runSequence('html', 'bs-reload');
  });
});

'use strict';

var gulp = require('gulp');
var runSequence = require('run-sequence');
var browserSync = require('browser-sync');

gulp.task('watch', ['browser-sync', 'html', 'sass', 'js'], function() {
  gulp.watch('./bundle.json', function() {
    runSequence('sanji-window-compiler', 'bs-reload');
  });
  gulp.watch('./src/window/*.scss', function() {
    runSequence('sass', function() {
      browserSync.reload({stream: true});
    });
  });
  gulp.watch('./src/window/*.js', function(event) {
    runSequence('js', 'bs-reload');
  });
  gulp.watch('./src/window/*.html', function() {
    runSequence('html', 'bs-reload');
  });
});

Modify sanji window compiler task

Modify sanji window compiler task


// Learn more about configuration: https://github.com/johnste/finicky/wiki/Configuration

module.exports = {
  defaultBrowser: "Safari"
}

// Use https://finicky-kickstart.now.sh to generate basic configuration
// Learn more about configuration options: https://github.com/johnste/finicky/wiki/Configuration

module.exports = {
  defaultBrowser: "Safari"
}

Update comments in default config

Update comments in default config


angular.module('yds').controller('Dashboard2Controller', ['$scope', 'DashboardService',
    function($scope, DashboardService) {
        var scope = $scope;

        // If the panels should be allowed to open
        scope.allowOpenPanel = false;

        // Default selected sector
        scope.selectedSector = "aidactivity";

        // Set initial panel titles
        scope.panelSectorTitle = "Choose your sector";
        scope.panelCountrySelectionTitle = "Choose countries";
        scope.panelTimePeriodTitle = "Choose time period of activities";
        scope.panelCategoryTitle = "Choose filter category";

        /**
         * Select a sector
         * @param newSector Sector to select
         */
        scope.setSelectedSector = function(newSector) {
            // Clear selected countries
            DashboardService.clearCountries(scope.selectedSector);
            DashboardService.clearCountries(newSector);

            // Select new sector
            scope.selectedSector = newSector;
        }
    }
]);

angular.module('yds').controller('Dashboard2Controller', ['$scope', 'DashboardService',
    function($scope, DashboardService) {
        var scope = $scope;

        // If the panels should be allowed to open
        scope.allowOpenPanel = false;

        // Default selected sector
        scope.selectedSector = "aidactivity";

        // Set initial panel titles
        scope.panelSectorTitle = "Choose your sector";
        scope.panelCountrySelectionTitle = "Choose countries";
        scope.panelTimePeriodTitle = "Choose time period of activities";
        scope.panelCategoryTitle = "Choose filter category";

        /**
         * Select a sector
         * @param newSector Sector to select
         */
        scope.setSelectedSector = function(newSector) {
            // Get country types for previously selected sector
            var map = DashboardService.getApiOptionsMapping(scope.selectedSector);

            // For country type of previous sector, clear its selected countries
            _.each(map, function(countryType) {
                DashboardService.clearCountries(countryType);
            });

            // Select new sector
            scope.selectedSector = newSector;
        }
    }
]);

Clear selected countries correctly when switching sectors


/*global describe, beforeEach, it*/
'use strict';
var assert = require('yeoman-generator').assert;

describe('ngbp module', function () {
  it('can be imported without blowing up', function () {
    var app = require('../app');
    assert(app !== undefined);
  });

  it('can load remove sub-generator', function () {
    var remove = require('../remove');
    assert(remove !== undefined);
  });
});

/*global describe, beforeEach, it*/
'use strict';
var assert = require('yeoman-generator').assert;

describe('ngbp module', function () {
  it('can be imported without blowing up', function () {
    var app = require('../app');
    assert(app !== undefined);
  });

  it('can load module sub-generator', function () {
    var module = require('../module');
    assert(module !== undefined);
  });

  it('can load remove sub-generator', function () {
    var remove = require('../remove');
    assert(remove !== undefined);
  });
});

Add test to check if module sub-generator loads


/*
    Tatoeba Project, free collaborativ creation of languages corpuses project
    Copyright (C) 2009  TATOEBA Project(should be changed)

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

*/
$(document).ready(function(){
	var lang = $("#randomLangChoice").val();
	if(lang == null) lang = '';

	loadRandom(lang);

	$("#showRandom").click(function(){
		lang = $("#randomLangChoice").val();
		loadRandom(lang);
	})
});

function loadRandom(lang){
	$(".random_sentences_set").html("<img src='/img/loading.gif' alt='loading'>");
	$(".random_sentences_set").load("http://" + self.location.hostname + ":" + self.location.port + "/" + $("#showRandom").attr("lang") + "/sentences/random/" + lang);
}

/*
    Tatoeba Project, free collaborativ creation of languages corpuses project
    Copyright (C) 2009  TATOEBA Project(should be changed)

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

*/
$(document).ready(function(){
	var lang = $("#randomLangChoice").val();
	if(lang == null) lang = '';

	loadRandom(lang);

	$("#showRandom").click(function(){
		lang = $("#randomLangChoice").val();
		loadRandom(lang);
	})
});

function loadRandom(lang){
	$(".random_sentences_set").html("<img src='/img/loading.gif' alt='loading'>");
	$(".random_sentences_set").load(
        "http://" + self.location.hostname + ":" + self.location.port + "/"
        + $("#showRandom").attr("lang")
        + "/sentences/random/" + lang + "/"
        + Math.random() // needed for IE
        // otherwise it always displays the same sentence when logged in...
    );
}

Fix for random sentence always displaying the same sentence in Internet Explorer 8.


import {createHistory} from 'history';
import {createStore, applyMiddleware, compose, combineReducers} from 'redux';
import thunkMiddleware from 'redux-thunk';
import {reduxReactRouter} from 'redux-react-router';

import routes from './routes';
import reducers from './reducers';

export default function createAppStore(initialState) {
  let finalCreateStore = createStore;

  if (process.env.NODE_ENV !== 'production') {
    const {devTools, persistState} = require('redux-devtools');
    const logger = require('redux-logger');

    finalCreateStore = compose(
      applyMiddleware(logger()),
      devTools(),
      persistState(window.location.href.match(/[?&]debug_session=([^&]+)\b/))
    )(finalCreateStore);
  }

  finalCreateStore = compose(
    applyMiddleware(
      thunkMiddleware
    ),
    reduxReactRouter({
      routes,
      createHistory,
    }),
  )(finalCreateStore);

  return finalCreateStore(combineReducers(reducers), initialState);
}

import {createHistory} from 'history';
import {createStore, applyMiddleware, compose, combineReducers} from 'redux';
import thunkMiddleware from 'redux-thunk';
import {reduxReactRouter} from 'redux-react-router';

import routes from './routes';
import reducers from './reducers';

export default function createAppStore(initialState) {
  let finalCreateStore = createStore;

  if (process.env.NODE_ENV === 'development') {
    const logger = require('redux-logger');

    finalCreateStore = compose(
      applyMiddleware(logger()),
    )(finalCreateStore);
  }

  if (process.env.NODE_ENV !== 'production') {
    const {devTools, persistState} = require('redux-devtools');

    finalCreateStore = compose(
      devTools(),
      persistState(window.location.href.match(/[?&]debug_session=([^&]+)\b/))
    )(finalCreateStore);
  }

  finalCreateStore = compose(
    applyMiddleware(
      thunkMiddleware
    ),
    reduxReactRouter({
      routes,
      createHistory,
    }),
  )(finalCreateStore);

  return finalCreateStore(combineReducers(reducers), initialState);
}

Disable redux logger in test environment


(function () {

'use strict';

angular.module('OpenSlidesApp.mediafiles.projector', ['OpenSlidesApp.mediafiles'])

.config([
    'slidesProvider',
    function(slidesProvider) {
        slidesProvider.registerSlide('mediafiles/mediafile', {
            template: 'static/templates/mediafiles/slide_mediafile.html'
        });
    }
])

.controller('SlideMediafileCtrl', [
    '$scope',
    'Mediafile',
    function($scope, Mediafile) {
        // load mediafile object
        var mediafile = Mediafile.get($scope.element.id);
        $scope.mediafile = mediafile;

        // Allow the elements to render properly
        setTimeout(function() {
            if ($scope.mediafile.is_pdf) {
                $scope.pdfName = mediafile.title;
                $scope.pdfUrl = mediafile.mediafileUrl;
            } else if ($scope.mediafile.is_video) {
                var player = angular.element.find('#video-player')[0];
                if ($scope.element.playing) {
                    player.play();
                } else {
                    player.pause();
                }
            }
        }, 0);
    }
]);

})();

(function () {

'use strict';

angular.module('OpenSlidesApp.mediafiles.projector', ['OpenSlidesApp.mediafiles'])

.config([
    'slidesProvider',
    function(slidesProvider) {
        slidesProvider.registerSlide('mediafiles/mediafile', {
            template: 'static/templates/mediafiles/slide_mediafile.html'
        });
    }
])

.controller('SlideMediafileCtrl', [
    '$scope',
    'Mediafile',
    function($scope, Mediafile) {
        // load mediafile object
        var mediafile = Mediafile.get($scope.element.id);
        $scope.mediafile = mediafile;

        // Allow the elements to render properly
        setTimeout(function() {
            if ($scope.mediafile) {
                if ($scope.mediafile.is_pdf) {
                    $scope.pdfName = mediafile.title;
                    $scope.pdfUrl = mediafile.mediafileUrl;
                } else if ($scope.mediafile.is_video) {
                    var player = angular.element.find('#video-player')[0];
                    if ($scope.element.playing) {
                        player.play();
                    } else {
                        player.pause();
                    }
                }
            }
        }, 0);
    }
]);

})();

Fix JS error after project pdf file.

Fix JS error after project pdf file.


function showGenericRelatedObjectLookupPopup(triggeringLink, ctArray)
{
    var realName = triggeringLink.id.replace(/^lookup_/, '');
    var name = id_to_windowname(realName);
    realName = realName.replace(/object_id/, 'content_type');
    var select = document.getElementById(realName);

    if (select.value === "") {
        alert("Select a content type first.");
        return false;
    }
    var selectedItem = select.item(select.selectedIndex).value;
    var href = triggeringLink.href.replace(/#/,'../../../'+ctArray[selectedItem]+"/?t=id");

    if (href.search(/\?/) >= 0) {
        href = href + '&_popup=1';
    } else {
        href = href + '?_popup=1';
    }
    var win = window.open(href, name, 'height=500,width=800,resizable=yes,scrollbars=yes');
    win.focus();
    return false;
}

function showGenericRelatedObjectLookupPopup(triggeringLink, ctArray)
{
    var realName = triggeringLink.id.replace(/^lookup_/, '');
    var name = id_to_windowname(realName);
    realName = realName.replace(/object_id/, 'content_type');
    var select = document.getElementById(realName);

    if (select.value === "") {
        alert("Select a content type first.");
        return false;
    }
    var selectedItem = select.item(select.selectedIndex).value;

    var href = triggeringLink.href.replace(/(\?|\#).*/,'../../../'+ctArray[selectedItem]);
    alert(href);
    if (href.search(/\?/) >= 0) {
        href = href + '&_popup=1';
    } else {
        href = href + '?_popup=1';
    }
    var win = window.open(href, name, 'height=500,width=800,resizable=yes,scrollbars=yes');
    win.focus();
    return false;
}

Update url so it take care of filters and remove `t=id` not sure why it was needed!


var AnnotationComponent = require('../../ui/AnnotationComponent');

function LinkComponent() {
  LinkComponent.super.apply(this, arguments);

  this._tagName = 'a';
}

AnnotationComponent.extend(LinkComponent, function LinkComponentPrototype() {
  this.render = function() {
    var el = AnnotationComponent.prototype.render.call(this);
    el.attr('href', this.props.node.url);

    var titleComps = [this.props.node.url];
    if (this.props.node.title) {
      titleComps.push(this.props.node.title);
    }

    return el.attr("title", titleComps.join(' | '));
  };

  this.didMount = function() {
    AnnotationComponent.prototype.didMount.call(this);
    var node = this.props.node;
    this.doc = node.getDocument();
    this.doc.getEventProxy('path').connect(this, [node.id, 'title'], this.rerender);
    this.doc.getEventProxy('path').connect(this, [node.id, 'url'], this.rerender);
  };

  this.dispose = function() {
    AnnotationComponent.prototype.dispose.call(this);
    this.doc.getEventProxy('path').disconnect(this);
  };
});

module.exports = LinkComponent;

"use strict";

var AnnotationComponent = require('../../ui/AnnotationComponent');

function LinkComponent() {
  LinkComponent.super.apply(this, arguments);

  this._tagName = 'a';
}

AnnotationComponent.extend(LinkComponent, function LinkComponentPrototype() {
  this.render = function() {
    var el = AnnotationComponent.prototype.render.call(this);
    el.attr('href', this.props.node.url);

    var titleComps = [this.props.node.url];
    if (this.props.node.title) {
      titleComps.push(this.props.node.title);
    }

    return el.attr("title", titleComps.join(' | '));
  };

  this.didMount = function() {
    AnnotationComponent.prototype.didMount.call(this);
    var node = this.props.node;
    node.connect(this, {'title:changed': this.rerender});
    node.connect(this, {'url:changed': this.rerender});
  };

  this.dispose = function() {
    AnnotationComponent.prototype.dispose.call(this);
    var doc = this.props.node.getDocument();
    doc.getEventProxy('path').disconnect(this.props.node);
    this.props.node.disconnect(this);
  };
});

module.exports = LinkComponent;

Refactor disposal of event handlers on link component


var vow = require('vow'),
    vowNode = require('vow-node'),
    childProcess = require('child_process'),
    fs = require('fs'),
    exec = vowNode.promisify(childProcess.exec),
    readFile = vowNode.promisify(fs.readFile),
    writeFile = vowNode.promisify(fs.writeFile);
    version = process.argv.slice(2)[0] || 'patch';

exec('git pull')
    .then(() => exec('npm i'))
    .then(() => exec('npm run build'))
    .then(() => exec('npm version ' + version))
    .then(() => vow.all([
        readFile('package.json', 'utf8'),
        readFile(__dirname + '/distHeaderTmpl.txt', 'utf8'),
        readFile('dist/vidom.js'),
        readFile('dist/vidom.min.js')
    ]))
    .spread((packageContent, distHeaderTmpl, distContent, distMinContent) => {
        version = JSON.parse(packageContent).version;

        var distHeader = distHeaderTmpl.replace('${VERSION}', version);

        return vow.all([
            writeFile('dist/vidom.js', distHeader + distContent),
            writeFile('dist/vidom.min.js', distHeader + distMinContent)
        ]);
    })
    .then(() => exec('git push --follow-tags'))
    .then(() => exec('npm publish'))
    .then(() => {
        console.log(`version ${version} has just been released`);
    })
    .done();

var vow = require('vow'),
    vowNode = require('vow-node'),
    childProcess = require('child_process'),
    fs = require('fs'),
    exec = vowNode.promisify(childProcess.exec),
    readFile = vowNode.promisify(fs.readFile),
    writeFile = vowNode.promisify(fs.writeFile);
    version = process.argv.slice(2)[0] || 'patch';

exec('git pull')
    .then(() => exec('npm i'))
    .then(() => exec('npm run build'))
    .then(() => exec(`npm version ${version}`))
    .then(() => vow.all([
        readFile('package.json', 'utf8'),
        readFile(__dirname + '/distHeaderTmpl.txt', 'utf8'),
        readFile('dist/vidom.js'),
        readFile('dist/vidom.min.js')
    ]))
    .spread((packageContent, distHeaderTmpl, distContent, distMinContent) => {
        version = JSON.parse(packageContent).version;

        var distHeader = distHeaderTmpl.replace('${VERSION}', version);

        return vow.all([
            writeFile('dist/vidom.js', distHeader + distContent),
            writeFile('dist/vidom.min.js', distHeader + distMinContent)
        ]);
    })
    .then(() => exec('git push --follow-tags'))
    .then(() => exec('npm publish' + (version.includes('rc')? ' --tag next' : '')))
    .then(() => {
        console.log(`version ${version} has just been released`);
    })
    .done();

Add ability to publish rc versions

Add ability to publish rc versions


var base_url_prod="http://147.83.7.157:8080"
var App = angular.module('messages', []);

App.controller('controller1', ['$scope', '$http', function($scope, $http) {

    var refresh = function() {
        $http.get(base_url_prod+'/messages/carlos').success(function (response) {
            console.log("Acabo de recibir los msg");
            console.log(response);
            $scope.messages = response;
        });
    }
    refresh();

    $scope.sendMessage = function() {
        console.log("ANTES DE ENVIARLO AL SERVIDOR;")
        console.log($scope.message);
        $http.post(base_url_prod+'/addmessage', $scope.message).success(function(response) {
            console.log($scope.message);
            console.log("RESPUESTA");
            console.log(response);
            refresh();
        });
    };
}]);

var base_url_prod="http://147.83.7.157:8080"
var App = angular.module('messages', []);

App.controller('controller1', ['$scope', '$http', function($scope, $http) {

    var refresh = function() {
        $http.get(base_url_prod+'/messages/carlos').success(function (response) {
            console.log("Acabo de recibir los msg");
            console.log(response);
            $scope.messages = response;
        });
    }
    refresh();

    $scope.sendMessage = function() {
        console.log("ANTES DE ENVIARLO AL SERVIDOR;")
        console.log($scope.message);
        $http.post(base_url_prod+'/addmessage', $scope.message).success(function(response) {
            console.log($scope.message);
            console.log("RESPUESTA");
            console.log(response);
            $scope.message="";
            refresh();
        });
    };
}]);

Delete the inputs after send the message


//"use strict";

// mocha tests:
describe('searchController', function () {
  var scope;
  var searchFactory;
  var ctrl;

  beforeEach(module('app'));

  beforeEach(inject(function($rootScope, $controller, _searchFactory_) {
    scope = $rootScope.$new();
    ctrl = $controller('searchController', {$scope: scope});
    searchFactory = _searchFactory_;
  }));

  describe('search function', function () {
    beforeEach(function () {
      var searchResults = {user: 'example artist', track: 'example track'};
      scope.$apply(function () {
        scope.test = 'test search text';
      });
      sinon.stub(searchFactory, 'getSearchResults', function() {
        return searchResults;
      });
    });

    it('should return search results when search() is called', function () {
      scope.search();
      expect(scope.searchResults).to.deep.equal({user: 'example artist', track: 'example track'});
    });
  });

});

describe('searchFactory', function () {
  beforeEach(module('app'));

  var searchFactory;
  var $window;
  var $httpBackend;

  beforeEach(inject(function(_searchFactory_, _$window_, _$httpBackend_) {
    searchFactory = _searchFactory_;
    $window = _$window_;
    $httpBackend = _$httpBackend_;
  }));

  it('should call the youtube api', function () {
    $httpBackend.expect('GET', 'url').respond('['fake': 'example']);

    $httpBackend.flush();

    //assertion goes here;
  });

});

//"use strict";

// mocha tests:
describe('searchController', function () {
  var scope;
  var searchFactory;
  var ctrl;

  beforeEach(module('app'));

  beforeEach(inject(function($rootScope, $controller, _searchFactory_) {
    scope = $rootScope.$new();
    ctrl = $controller('searchController', {$scope: scope});
    searchFactory = _searchFactory_;
  }));

  describe('search function', function () {
    beforeEach(function () {
      var searchResults = {items:[{id: {videoId:'example id'}, snippet: {title: 'example title', thumbnails: {medium: {url: 'thumbnailUrl'}}}}]};
      scope.$apply(function () {
        scope.test = 'test search text';
      });
      sinon.stub(searchFactory, 'getSearchResults', function() {
        return {
          then: function (cb) {
            cb(searchResults);
          }
        }
      });
    });

    it('should return search results when search() is called', function () {
      scope.search();
      expect(scope.searchResults[0].id).to.equal('example id');
    });
  });

});

// describe('searchFactory', function () {
//   beforeEach(module('app'));

//   var searchFactory;
//   var $window;
//   var $httpBackend;

//   beforeEach(inject(function(_searchFactory_, _$window_, _$httpBackend_) {
//     searchFactory = _searchFactory_;
//     $window = _$window_;
//     $httpBackend = _$httpBackend_;
//   }));

  // it('should call the youtube api', function () {
  //   $httpBackend.expect('GET', 'url').respond('['fake': 'example']);

  //   $httpBackend.flush();

  //   //assertion goes here;
  // });

//});

Make fake promise-returning function for test stub


minerva.models.WmsSourceModel = minerva.models.SourceModel.extend({

    createSource: function (name, baseURL, projection) {
        var params = {
            name: name,
            baseURL: baseURL,
            projection: projection
        };
        girder.restRequest({
            path: '/minerva_source/wms_source',
            type: 'POST',
            data: params,
            error: null // ignore default error behavior (validation may fail)
        }).done(_.bind(function (resp) {
            this.set(resp);
            this.trigger('m:saved');
        }, this)).error(_.bind(function (err) {
            this.trigger('m:error', err);
        }, this));

        return this;
    }
});

minerva.models.WmsSourceModel = minerva.models.SourceModel.extend({

    createSource: function (name, baseURL) {
        var params = {
            name: name,
            baseURL: baseURL
        };
        girder.restRequest({
            path: '/minerva_source/wms_source',
            type: 'POST',
            data: params,
            error: null // ignore default error behavior (validation may fail)
        }).done(_.bind(function (resp) {
            this.set(resp);
            this.trigger('m:saved');
        }, this)).error(_.bind(function (err) {
            this.trigger('m:error', err);
        }, this));

        return this;
    }
});

Remove projection from client WMSSource Model


/**
 * Class Sliders
 */
Humble( function () {

    var Sliders = function (node, model) {

        this.model      = model;
        this.node       = node;
        this.sliders    = {};

        this.render();
    }

    Sliders.prototype = {

        render : function () {

            var items   = this.model.getItems(),
                node    = this.node;

            _.each(items, function (item, key) {
                var slider = this._renderSlider(item);
                node.append(slider);
                slider.data('key', key);
                sliders[key] = slider;
            }, this);

            this.bind();
        },

        _renderSlider : function (item) {

            var slider = $('

'),
                widget = $('

'),
                total  = this.model.getTotalTaxes(),
                value  = item['mycosti'],
                title  = item['dimensionName'],
                config;

            slider.append('
$'+value+'
');
            slider.append('
'+title+'
');
            slider.append(widget);

            config = {
                max : total,
                value : value
            };

            widget.slider(config);

            return slider;
        },

        bind : function () {
            this.node.delegate('.slider', 'slide', {this : this}, this._onSlide);
        },

        _onSlide : function (e, ui) {
            $(this).find('.mycosti').html('$'+ui.value);
        }
    }

    Humble.Sliders = Sliders;
});

/**
 * Class Sliders
 */
Humble( function () {

    var Sliders = function (node, model) {

        this.model      = model;
        this.node       = node;
        this.sliders    = {};

        this.render();
    }

    Sliders.prototype = {

        render : function () {

            var items   = this.model.getItems(),
                node    = this.node;

            _.each(items, function (item, key) {
                var slider = this._renderSlider(item);
                node.append(slider);
                slider.data('key', key);
                sliders[key] = slider;
            }, this);

            this.bind();
        },

        _renderSlider : function (item) {

            var slider = $('

'),
                widget = $('

'),
                total  = this.model.getTotalTaxes(),
                value  = item['mycosti'],
                title  = item['dimensionName'],
                config;

            slider.append('
$'+value+'
');
            slider.append('
'+title+'
');
            slider.append(widget);

            config = {
                max : total,
                value : value
            };

            widget.slider(config);

            return slider;
        },

        bind : function () {
            this.node.delegate('.slider', 'slide', {this : this}, this._onSlide);
        },

        _onSlide : function (e, ui) {

            var key     = $(this).data('key'),
                sliders = e.data.this;

            $(this).find('.mycosti').html('$'+ui.value);
            sliders.model.set(key, 'mycosti', ui.value);
        }
    }

    Humble.Sliders = Sliders;
});

Update model with new mycosti value.

Update model with new mycosti value.


const mix = require('laravel-mix');
const fs = require('fs');

mix.config.detectHotReloading()
if (mix.config.hmr) {
    // There's a bug with Mix/copy plugin which prevents HMR from working:
    // https://github.com/JeffreyWay/laravel-mix/issues/150
    console.log('In HMR mode. If assets are missing, Ctr+C and run `yarn dev` first.');

    // Somehow public/hot isn't being removed by Mix. We'll handle it ourselves.
    process.on('SIGINT', () => {
        try {
            fs.unlinkSync(mix.config.publicPath + '/hot');
        } catch (e) {}
        process.exit();
    });
} else {
    mix.copy('resources/assets/img', 'public/img', false)
        .copy('node_modules/font-awesome/fonts', 'public/fonts');
}

mix.js('resources/assets/js/app.js', 'public/js')
    .sass('resources/assets/sass/app.scss', 'public/css');

if (mix.config.inProduction) {
    mix.version();
    mix.disableNotifications();
}

const mix = require('laravel-mix');
const fs = require('fs');

mix.config.detectHotReloading()
if (mix.config.hmr) {
    // There's a bug with Mix/copy plugin which prevents HMR from working:
    // https://github.com/JeffreyWay/laravel-mix/issues/150
    console.log('In HMR mode. If assets are missing, Ctr+C and run `yarn dev` first.');

    // Somehow public/hot isn't being removed by Mix. We'll handle it ourselves.
    process.on('SIGINT', () => {
        try {
            fs.unlinkSync(mix.config.publicPath + '/hot');
        } catch (e) {}
        process.exit();
    });
} else {
    mix.copy('resources/assets/img', 'public/img', false)
        .copy('node_modules/font-awesome/fonts', 'public/fonts', false);
}

mix.js('resources/assets/js/app.js', 'public/js')
    .sass('resources/assets/sass/app.scss', 'public/css');

if (mix.config.inProduction) {
    mix.version();
    mix.disableNotifications();
}

Copy files and dirs recursively

Copy files and dirs recursively


/**
 *
 *
 * @copyright 2017 Government of Canada
 * @license MIT
 * @author igboyes
 *
 */

import { WS_UPDATE_STATUS, FIND_HMMS, GET_HMM } from "../actionTypes";

const initialState = {
    list: null,
    detail: null,
    process: null
};

const hmmsReducer = (state = initialState, action) => {

    switch (action.type) {

        case WS_UPDATE_STATUS:
            if (action.data.id === "hmm_install") {
                return {
                    ...state,
                    process: action.data.process,
                    ready: action.data.ready,
                    size: action.data.download_size
                };
            }

            return state;

        case FIND_HMMS.SUCCEEDED:
            return {
                ...state,
                fileExists: action.data.file_exists,
                list: action.data.documents,
                page: action.data.page,
                pageCount: action.data.page_count,
                totalCount: action.data.total_count
            };

        case GET_HMM.SUCCEEDED:
            return {...state, detail: action.data};

        default:
            return state;

    }
};

export default hmmsReducer;

/**
 *
 *
 * @copyright 2017 Government of Canada
 * @license MIT
 * @author igboyes
 *
 */

import { WS_UPDATE_STATUS, FIND_HMMS, GET_HMM } from "../actionTypes";

const initialState = {
    list: null,
    detail: null,
    process: null
};

const hmmsReducer = (state = initialState, action) => {

    switch (action.type) {

        case WS_UPDATE_STATUS:
            if (action.data.id === "hmm_install") {
                return {
                    ...state,
                    process: action.data.process,
                    ready: action.data.ready,
                    size: action.data.download_size
                };
            }

            return state;

        case FIND_HMMS.SUCCEEDED:
            return {
                ...state,
                fileExists: action.data.file_exists,
                list: action.data.documents,
                page: action.data.page,
                pageCount: action.data.page_count,
                totalCount: action.data.total_count
            };

        case GET_HMM.REQUESTED:
            return {...state, detail: null};

        case GET_HMM.SUCCEEDED:
            return {...state, detail: action.data};

        default:
            return state;

    }
};

export default hmmsReducer;

Set hmm detail state to null on GET_HMM.REQUESTED


module.exports = (router) => {
  // router is props.history from a component served by react router
  let cookies = {};
  for (let cookie of document.cookie.split(';')) {
    let c = cookie.trim().split('=');
    cookies[c[0]] = c[1];
  }

  if (!(cookies.fridgrSesh && cookies.fridgrSesh.userId && cookies.fridgrSesh.houseId)) {
    router.push('/login');
  }

};

import {parse} from 'cookie';
module.exports = (router) => {
  // router is props.history from a component served by react router
  let cookies = parse(document.cookie);

  if (!(cookies.fridgrSesh && cookies.fridgrSesh.userId && cookies.fridgrSesh.houseId)) {
    router.push('/login');
  }

};

Refactor auth to use npm cookie parse

Refactor auth to use npm cookie parse


var eve_image;
(function createProxFunc() {
	var webpProxySupport = (waitlist.base.getMetaData("eve-image-server-webp") === "True");
	var webpBrowserSupport = (waitlist.base.getMetaData("browser-webp") === "True");

	console.log("Proxy Supports WebP: ", webpProxySupport);
	console.log("Browser Supports WebP:", webpBrowserSupport);

	eve_image = function(path, suffix) {
		if (webpProxySupport && webpBrowserSupport) {
			suffix = "webp";
		}
		return waitlist.base.getMetaData("eve-image-server").replace("${ path }", path).replace("${ suffix }", suffix);
	}
})();

var eve_image;
(function createProxFunc() {
	var webpProxySupport = (waitlist.base.getMetaData("eve-image-server-webp") === "True");
	var webpBrowserSupport = (waitlist.base.getMetaData("browser-webp") === "True");
	var proxyUrl = waitlist.base.getMetaData("eve-image-server");

	console.log("Proxy Supports WebP: ", webpProxySupport);
	console.log("Browser Supports WebP:", webpBrowserSupport);

	eve_image = function(path, suffix) {
		if (webpProxySupport && webpBrowserSupport) {
			suffix = "webp";
		}
		return proxyUrl.replace("${ path }", path).replace("${ suffix }", suffix);
	}
})();

Update get proxy url on first run to minimise repeated function calls.


var Bind = require("github/jillix/bind");
var Events = require("github/jillix/events");

module.exports = function(config) {

    var self = this;
    Events.call(self, config);

    var $iframe = $("iframe", self.dom);
    $("form", self.dom).on("submit", function () {
        if (!$(this).find("input[type='file']").val()) {
            return;
        }
        $iframe.off("load");
        $iframe.on("load", function () {
            var result = $(this.contentWindow.document).text();
            if (!result) { return; }

            try {
                result = JSON.parse(result);
            } catch (e) {
                // nothing to do
            }

            self.emit("fileUploaded", result);

            var $iframeClone = $iframe.clone();
            $iframe.attr("src", "");

            var $inputTypeFile = $("input[type=file]", self.dom);
            $inputTypeFile.replaceWith($inputTypeFile.clone(true));
        });
    });

    self.emit("ready", config);
};

var Bind = require("github/jillix/bind");
var Events = require("github/jillix/events");

module.exports = function(config) {

    var self = this;
    Events.call(self, config);

    var $iframe = $("iframe", self.dom);
    $("form", self.dom).on("submit", function () {
        var $form = $(this);
        $iframe.off("load");
        $iframe.on("load", function () {
            var result = $(this.contentWindow.document).text();
            if (!result) { return; }

            var $inputFile = $form.find("input[type='file']");
            if (!$inputFile.val()) {
                return;
            }

            try {
                result = JSON.parse(result);
            } catch (e) {
                // nothing to do
            }

            self.emit("fileUploaded", result);

            var $iframeClone = $iframe.clone();
            $iframe.attr("src", "");

            $inputFile.replaceWith($inputFile.clone(true));
        });
    });

    self.emit("ready", config);
};

Verify if any file was choosen inside of the iframe load handler.


import styled, { css } from 'styled-components'
import rem from 'polished/lib/helpers/rem'
import { mobile } from '../../utils/media'
import { violetRed, gold } from '../../utils/colors'

const Sidebar = styled.nav`
  position: fixed;
  display: block;
  z-index: 1;

  left: 0;
  top: 0;
  bottom: 0;
  right: auto;

  width: ${rem(300)};
  height: 100%;
  background: linear-gradient(20deg, ${violetRed}, ${gold});
  box-sizing: border-box;
  color: white;
  overflow-y: scroll;

  ${mobile(css`
    bottom: auto;
    right: 0;
    height: auto;
    width: 100%;
    overflow-y: visible;
  `)}
`

export default Sidebar

import styled, { css } from 'styled-components'
import rem from 'polished/lib/helpers/rem'
import { mobile } from '../../utils/media'
import { violetRed, gold } from '../../utils/colors'
import captureScroll from '../CaptureScroll'

const Sidebar = styled.nav`
  position: fixed;
  display: block;
  z-index: 1;

  left: 0;
  top: 0;
  bottom: 0;
  right: auto;

  width: ${rem(300)};
  height: 100%;
  background: linear-gradient(20deg, ${violetRed}, ${gold});
  box-sizing: border-box;
  color: white;
  overflow-y: scroll;

  ${mobile(css`
    bottom: auto;
    right: 0;
    height: auto;
    width: 100%;
    overflow-y: visible;
  `)}
`

export default captureScroll(Sidebar)

Add scroll capture to sidebar

Add scroll capture to sidebar


const requestsController = require('../../controllers/requests.js');
const requestHelper = require('../../helpers/requestHelper.js');

module.exports = {
  '/:request/top/:size': {
    get: function get(req, res, cb) {
    	const size = parseInt(req.params.size);
    	res.setHeader('Access-Control-Allow-Origin', '*');

    	if (typeof size == 'number' && size > 5) {
    		requestsController.getRowRequest(req.params.request, size, req, res);
    	} else {
    		res.json({ success: false, message: 'Invalid size parameter'});
    	}
    },
  },
};

const requestsController = require('../../controllers/requests.js');
const requestHelper = require('../../helpers/requestHelper.js');

module.exports = {
  '/:request/top/:size': {
    get: function get(req, res, cb) {
    	const size = parseInt(req.params.size);
    	res.setHeader('Access-Control-Allow-Origin', '*');

    	if (typeof size == 'number' && size > 5) {
    		requestsController.getRowRequest(req.params.request, size, req, res);
    	} else {
    		res.json({ success: false, message: 'Invalid size parameter'});
    	}
    },
  },
  '/game/:type': {
    get: function get (req, res, cb) {
        requestsController.getGameRequest(req.params.type, req, res);
    },
  },
};

Add routes for game queries

Add routes for game queries


Allow fonts from data: origins

Allow fonts from data: origins


import React from 'react';
import { useEffect, useRef, useState } from '@storybook/client-api';

export default {
  title: 'Hooks',
};

export const Checkbox = () => {
  const [on, setOn] = useState(false);
  return (
    <label>
      <input type="checkbox" checked={on} onChange={(e) => setOn(e.target.checked)} />
      On
    </label>
  );
};

export const Input = () => {
  const [text, setText] = useState('foo');
  return <input value={text} onChange={(e) => setText(e.target.value)} />;
};

export const Effect = () => {
  const ref = useRef();
  useEffect(() => {
    if (ref.current != null) {
      ref.current.style.backgroundColor = 'yellow';
    }
  });

  return (
    <button type="button" ref={ref}>
      I should be yellow
    </button>
  );
};

export const ReactHookCheckbox = () => {
  const [on, setOn] = React.useState(false);
  return (
    <label>
      <input type="checkbox" checked={on} onChange={(e) => setOn(e.target.checked)} />
      On
    </label>
  );
};

import React from 'react';
import { useEffect, useRef, useState } from '@storybook/client-api';

export default {
  title: 'Core/Hooks',
};

export const Checkbox = () => {
  const [on, setOn] = useState(false);
  return (
    <label>
      <input type="checkbox" checked={on} onChange={(e) => setOn(e.target.checked)} />
      On
    </label>
  );
};

export const Input = () => {
  const [text, setText] = useState('foo');
  return <input value={text} onChange={(e) => setText(e.target.value)} />;
};

export const Effect = () => {
  const ref = useRef();
  useEffect(() => {
    if (ref.current != null) {
      ref.current.style.backgroundColor = 'yellow';
    }
  });

  return (
    <button type="button" ref={ref}>
      I should be yellow
    </button>
  );
};

export const ReactHookCheckbox = () => {
  const [on, setOn] = React.useState(false);
  return (
    <label>
      <input type="checkbox" checked={on} onChange={(e) => setOn(e.target.checked)} />
      On
    </label>
  );
};

MOVE hooks to core group

MOVE hooks to core group


/**
 * Stack class to create Stack objects for browserHistory
 * underscore denotes that these variables are private and should not be manipulated externally
 * top method returns the current position*/
class Stack{
    constructor(){
        this._storage = {};
        this._position = -1 //0 indexed when we add items
    }
    top(){
        return this._position;
    }
}

let browserHistory = new Stack();

Create stack class and constructor

Create stack class and constructor


'use strict';
describe('loginController tests', function () {
    var scope, ctrl, authServices, location;

    function authServicesMock() {
        var mock = {
            succeed: true
        };

        mock.login = function() {
            return {
                then: function(r, e) {
                    if (mock.succeed) {
                        r();
                    } else {
                        e({ error_description: 'Error' });
                    }
                }
            }
        };
        return mock;
    };

    beforeEach(function() {
        module('AzureLinkboardApp');
        module('linkboardControllers');

        inject(function ($rootScope, $injector, $controller) {
            authServices = authServicesMock();
            location = $injector.get('$location');
            scope = $rootScope.$new();
            ctrl = $controller("loginController", {
                $scope: scope,
                $location: location,
                authService: authServices
            });
        });
    });

    it("should have empty login data", function () {
        expect(scope.loginData).toBeDefined();
        expect(scope.loginData.userName).toBe("");
        expect(scope.loginData.password).toBe("");
    });

    it("should start with an empty message", function () {
        expect(scope.message).toBe("");
    });

    it("login should go to links page", function () {
        scope.login();
        expect(location.path()).toBe('/links');
    });

    it("failed login should set message", function () {
        authServices.succeed = false;
        scope.login();
        expect(scope.message).toBe("Error");
    });
});

'use strict';
describe('loginController tests', function () {
    var scope, ctrl, authServices, location;

    function authServicesMock() {
        var mock = {
            succeed: true
        };

        mock.login = function() {
            return {
                then: function(r, e) {
                    if (mock.succeed) {
                        r();
                    } else {
                        e({ error_description: 'Error' });
                    }
                }
            }
        };
        return mock;
    };

    beforeEach(function() {
        module('AzureLinkboardApp');
        module('linkboardControllers');

        inject(function ($rootScope, $injector, $controller) {
            authServices = authServicesMock();
            location = $injector.get('$location');
            scope = $rootScope.$new();
            ctrl = $controller("loginController", {
                $scope: scope,
                $location: location,
                authService: authServices
            });
        });
    });

    it("should have empty login data", function () {
        expect(scope.loginData).toBeDefined();
        expect(scope.loginData.userName).toBe("");
        expect(scope.loginData.password).toBe("");
    });

    it("should start with an empty message", function () {
        expect(scope.message).toBe("");
    });

    it("login should go to links page", function () {
        scope.login();
        expect(location.path()).toBe('/links');
    });

    it("failed login should set message", function () {
        authServices.succeed = false;
        scope.login();
        expect(scope.message).toBe("Error");
    });

    it("should break the build", function() {
        expect(false).toBeTruthy();
    });
});

Test commit to see if a failing unit test breaks the build


let pkg = require('./package.json');

module.exports = {
    baseUrl: process.env.NODE_ENV === 'production' ? '/guess-them-all/' : '/',

    pwa: {
        name: pkg.description,
        themeColor: '#448AFF',
        appleMobileWebAppCapable: true,

        // configure the workbox plugin
        workboxOptions: {
            swDest: 'sw.js',
            runtimeCaching: [
                {
                    urlPattern: new RegExp('fonts.(gstatic|googleapis).com/(.*)'),
                    handler: 'staleWhileRevalidate',
                },
            ],
            navigateFallback: '/',
            directoryIndex: 'index.html',
        },
    },
};

let pkg = require('./package.json');

module.exports = {
    baseUrl: process.env.NODE_ENV === 'production' ? '/guess-them-all/' : '/',

    pwa: {
        name: pkg.description,
        themeColor: '#448AFF',
        appleMobileWebAppCapable: true,

        // configure the workbox plugin
        workboxOptions: {
            swDest: 'sw.js',
            runtimeCaching: [
                {
                    urlPattern: new RegExp('fonts.(gstatic|googleapis).com/(.*)'),
                    handler: 'staleWhileRevalidate',
                },
            ],
            navigateFallback: '/',
            directoryIndex: 'index.html',
            clientsClaim: true,
            skipWaiting: true,
        },
    },
};

Add clientsClaim and skipWaiting options


exports.init = function(grunt) {
  var exports = {};

  exports.casperjs = function(filepath, options, callback) {

    var command = 'casperjs test',
        exec = require('child_process').exec;

    // Add options documented in the following web site:
    //   http://casperjs.org/testing.html
    if (options.xunit) {
      command += ' --xunit=' + options.xunit;
    }

    if (options.direct) {
      command += ' --direct';
    }

    if (options.includes) {
      command += ' --includes=' + options.includes.join(',');
    }

    if (options.logLevel) {
      command += ' --log-level=' + options.logLevel;
    }

    if (options.pre) {
      command += ' --pre=' + options.pre.join(',');
    }

    if (options.post) {
      command += ' --post=' + options.post.join(',');
    }

    command += " " + filepath;

    grunt.log.write("Command: " + command);

    function puts(error, stdout, stderr) {
      grunt.log.write('\nRunning tests from "' + filepath + '":\n');
      grunt.log.write(stdout);

      if ( error !== null ) {
        callback(error);
      } else {
        callback();
      }
    }

    exec(command, puts);

  };

  return exports;
};

exports.init = function(grunt) {
  var exports = {};

  exports.casperjs = function(filepath, options, callback) {

    var command = 'casperjs test',
        exec = require('child_process').exec;

    // Add options documented in the following web site:
    //   http://casperjs.org/testing.html
    if (options.xunit) {
      command += ' --xunit=' + options.xunit;
    }

    if (options.direct) {
      command += ' --direct';
    }

    if (options.includes) {
      command += ' --includes=' + options.includes.join(',');
    }

    if (options.logLevel) {
      command += ' --log-level=' + options.logLevel;
    }

    if (options.engine) {
      command += ' --engine=' + options.engine;
    }

    if (options.pre) {
      command += ' --pre=' + options.pre.join(',');
    }

    if (options.post) {
      command += ' --post=' + options.post.join(',');
    }

    command += " " + filepath;

    grunt.log.write("Command: " + command);

    function puts(error, stdout, stderr) {
      grunt.log.write('\nRunning tests from "' + filepath + '":\n');
      grunt.log.write(stdout);

      if ( error !== null ) {
        callback(error);
      } else {
        callback();
      }
    }

    exec(command, puts);

  };

  return exports;
};

Add new 'engine' option of Casperjs

Add new 'engine' option of Casperjs


function findBestIcon(iconPaths) {
    return new Promise(function (resolve, reject) {
        resolve(iconPaths[0]);
    });
}

module.exports = findBestIcon;

const fs = require('fs');

function getFileSize(filePath) {
    return new Promise((resolve, reject) => {
        fs.stat(filePath, function(error, fileInfo) {
            if (error) {
                reject(error);
                return;
            }

            const fileSizeKB = fileInfo.size / 1000;
            resolve({
                sizeKB: fileSizeKB,
                path: filePath
            });
        });
    });
}

function getFileSizes(filePaths) {
    const getFileSizePromises = filePaths.map(getFileSize);
    return Promise.all(getFileSizePromises)
}

function sortFileBySizes(fileSizes) {
    return fileSizes.sort(function(a, b){
        if (a.sizeKB < b.sizeKB) {
            return 1;
        } else {
            return -1;
        }
    });
}

function findBestIcon(iconPaths) {
    return getFileSizes(iconPaths).then(fileSizes => {
        // sort descending
        const sortedFileSizes = sortFileBySizes(fileSizes);
        return sortedFileSizes[0].path;
    });
}

module.exports = findBestIcon;

Return icon path with largest size

Return icon path with largest size


$(function () {
    const menu = $('.js-menu');
    const menuButton = $('.js-menu-toggle', menu);

    menuButton.on('click', (e) => {
        menu.toggleClass('is-open');
    })
});

$(function () {
    const menu = $('.js-menu');
    const menuButton = $('.js-menu-toggle', menu);

    menuButton.on('click', function(e) {
        menu.toggleClass('is-open');
    })
});

Remove arrow functon, causes precompile error on prod.


var grunt = require('grunt');

module.exports = {
  main: {
    options: {
      paths: grunt.file.expand('src/**'),
      sourceMap: true,
      sourceMapRootpath: '/'
    },
    files: {
      'demo/static/css/main.css': [
        'src/vendor/cf-concat/cf.less'
      ]
    }
  }
};

var grunt = require('grunt');

module.exports = {
  main: {
    options: {
      paths: grunt.file.expand('src/vendor/**'),
      sourceMap: true,
      sourceMapRootpath: '/'
    },
    files: {
      'demo/static/css/main.css': [
        'src/vendor/cf-concat/cf.less'
      ]
    }
  }
};

Adjust import paths for Less files

Adjust import paths for Less files


describe('pingPong', function() {
  it("returns ping for a number that is divisible by 3", function() {
    expect(pingPong(6)).to.equal("ping");
  });
  it("returns pong for a number that is divisible by 5", function() {
    expect(pingPong(10)).to.equal("pong");
  });
  it("returns pingpong for a number that is divisible by 3 and 5", function() {
    expect(pingPong(30)).to.equal("pingpong")
  });
  it("is false for a number that is not divisible by 3 or 5", function() {
    expect(!pingPong).to.equal(false);
  });
});

describe('isPingPong', function() {
  it("returns true for a number divisible by 3, 5, or 15", function() {
    expect(isPingPong(6)).to.equal(true);
  });
  it("returns false for a number not divisible by 3, 5, or 15", function() {
    expect(isPingPong(7)).to.equal(false);
  });
});

describe('pingPong', function() {
  it("is false for a number that is not divisible by 3 or 5", function() {
    expect(pingPong(7)).to.equal(false);
  });
  it("will run pingPongType if isPingPong is true", function() {
    expect(pingPong(6)).to.equal("ping");
  });
});

describe('pingPongType', function() {
  it("returns ping for a number that is divisible by 3", function() {
    expect(pingPongType(6)).to.equal("ping");
  });
  it("returns pong for a number that is divisible by 5", function() {
    expect(pingPongType(10)).to.equal("pong");
  });
  it("returns pingpong for a number that is divisible by 3 and 5", function() {
    expect(pingPongType(30)).to.equal("pingpong")
  });
});

describe('isPingPong', function() {
  it("returns true for a number divisible by 3, 5, or 15", function() {
    expect(isPingPong(6)).to.equal(true);
  });
  it("returns false for a number not divisible by 3, 5, or 15", function() {
    expect(isPingPong(7)).to.equal(false);
  });
});

Add new test called pingPongType

Add new test called pingPongType


var $ = document.getElementById.bind(document);
import {simplePassingTestCode} from './example-tests';
import {Controller as Main} from '../src/main/main-controller';
import {util} from './_util';
import {shortcuts as aceDefaultShortcuts} from './_aceDefaultShortcuts';

var shortcuts = aceDefaultShortcuts.concat([
  util.getShortcutObject([util.metaKey, 'S'], executeTestCode, 'Save+Run'),
  util.getShortcutObject(['Shift', 'F6'], refactoringRename, 'Rename (refactoring)')
]);

var main = new Main($('tddbin'), {
  initialContent: simplePassingTestCode,
  iframeSrcUrl: './mocha/spec-runner.html',
  shortcuts: shortcuts
});

function executeTestCode() {
  main.runEditorContent();
}
function refactoringRename() {
  main.turnOnRenameMode();
}

var $ = document.getElementById.bind(document);
import {simplePassingTestCode} from './example-tests';
import {Controller as Main} from '../src/main/main-controller';
import {util} from './_util';
import {shortcuts as aceDefaultShortcuts} from './_aceDefaultShortcuts';

var shortcuts = aceDefaultShortcuts.concat([
  util.getShortcutObject([util.metaKey, 'S'], executeTestCode, 'Save+Run'),
  util.getShortcutObject(['Shift', 'F6'], refactoringRename, 'Rename (refactoring)')
]);

var testRunner = getTestRunner();

var main = new Main($('tddbin'), {
  initialContent: simplePassingTestCode,
  iframeSrcUrl: `./${testRunner}/spec-runner.html`,
  shortcuts: shortcuts
});

function executeTestCode() {
  main.runEditorContent();
}
function refactoringRename() {
  main.turnOnRenameMode();
}

function getTestRunner() {
  var validTestRunners = ['mocha', 'jasmine'];
  var queryString = window.location.search;
  var testRunner = queryString.match(/test-runner=(\w+)/);
  if (testRunner.length === 2 && validTestRunners.indexOf(testRunner[1]) > -1) {
    return testRunner[1];
  }
  return 'mocha';
}

Allow parametrizing which test-runner to use, only jasmine/mocha allowed now.


#!/usr/bin/env node
require('source-map-support/register')

var Config = require("truffle-config");
var Command = require("./lib/command");
var TaskError = require("./lib/errors/taskerror");
var TruffleError = require("truffle-error");

var command = new Command(require("./lib/commands"));

// Hack to suppress web3 MaxListenersExceededWarning
// This should be removed when issue is resolved upstream:
// https://github.com/ethereum/web3.js/issues/1648
var listeners = process.listeners('warning');
listeners.forEach(listener => process.removeListener('warning', listener));

var options = {
  logger: console
};

const inputArguments = process.argv.slice(2);
const userWantsGeneralHelp = inputArguments[0] === 'help' &&  inputArguments.length === 1;

if (userWantsGeneralHelp) {
  command.displayGeneralHelp();
  process.exit(0);
}

command.run(inputArguments, options, function(err) {
  if (err) {
    if (err instanceof TaskError) {
      command.displayGeneralHelp();
    } else {
      if (err instanceof TruffleError) {
        console.log(err.message);
      } else if (typeof err == "number") {
        // If a number is returned, exit with that number.
        process.exit(err);
      } else {
        // Bubble up all other unexpected errors.
        console.log(err.stack || err.toString());
      }
    }
    process.exit(1);
  }
  process.exit(0);
});

#!/usr/bin/env node
require('source-map-support/register')

var Config = require("truffle-config");
var Command = require("./lib/command");
var TaskError = require("./lib/errors/taskerror");
var TruffleError = require("truffle-error");

var command = new Command(require("./lib/commands"));

// Hack to suppress web3 MaxListenersExceededWarning
// This should be removed when issue is resolved upstream:
// https://github.com/ethereum/web3.js/issues/1648
var listeners = process.listeners('warning');
listeners.forEach(listener => process.removeListener('warning', listener));

var options = {
  logger: console
};

const inputArguments = process.argv.slice(2);
const userWantsGeneralHelp = (inputArguments[0] === "help" || inputArguments[0] === "--help") &&
                             inputArguments.length === 1;

if (userWantsGeneralHelp) {
  command.displayGeneralHelp();
  process.exit(0);
}

command.run(inputArguments, options, function(err) {
  if (err) {
    if (err instanceof TaskError) {
      command.displayGeneralHelp();
    } else {
      if (err instanceof TruffleError) {
        console.log(err.message);
      } else if (typeof err == "number") {
        // If a number is returned, exit with that number.
        process.exit(err);
      } else {
        // Bubble up all other unexpected errors.
        console.log(err.stack || err.toString());
      }
    }
    process.exit(1);
  }
  process.exit(0);
});

Allow general help to be accessible using --help in the command line


/**
 * Typescript webpack block.
 *
 * @see https://github.com/s-panferov/awesome-typescript-loader
 */

const { CheckerPlugin, TsConfigPathsPlugin } = require('awesome-typescript-loader')

module.exports = typescript

/**
 * @param {object}   [advancedPathResolution]                  See https://github.com/s-panferov/awesome-typescript-loader#advanced-path-resolution-in-typescript-20
 * @param {object} [options.tsconfig]
 * @param {object} [options.compiler]
 * @return {Function}
 */
function typescript (options) {
  return (context) => ({
    resolve: {
      extensions: ['.ts', '.tsx']
    },
    module: {
      loaders: [
        {
          test: context.fileType('application/x-typescript'),
          loaders: [
            'awesome-typescript-loader'
          ]
        }
      ]
    },
    plugins: options ? [new CheckerPlugin(), new TsConfigPathsPlugin({ tsconfig: options.tsconfig, compiler: options.compiler })] : [new CheckerPlugin()]
  })
}

/**
 * Typescript webpack block.
 *
 * @see https://github.com/s-panferov/awesome-typescript-loader
 */

const { CheckerPlugin, TsConfigPathsPlugin } = require('awesome-typescript-loader')

module.exports = typescript

/**
 * @param {object} [advancedPathResolution]                  See https://github.com/s-panferov/awesome-typescript-loader#advanced-path-resolution-in-typescript-20
 * @param {object} [options.tsconfig]
 * @param {object} [options.compiler]
 * @return {Function}
 */
function typescript (options) {
  const setter = (context) => ({
    resolve: {
      extensions: ['.ts', '.tsx']
    },
    module: {
      loaders: [
        {
          test: context.fileType('application/x-typescript'),
          loaders: [
            'awesome-typescript-loader'
          ]
        }
      ]
    },
    plugins: [
      new CheckerPlugin()
    ].concat(
      options ? [
        new TsConfigPathsPlugin({ tsconfig: options.tsconfig, compiler: options.compiler })
      ] : []
    )
  })

  return Object.assign(setter, { pre })
}

function pre (context) {
  if ('application/x-typescript' in context.fileType.all()) {
    // file type is already there
  } else {
    context.fileType.add('application/x-typescript', /\.(ts|tsx)$/)
  }
}

Add pre hook to typescript block

Add pre hook to typescript block

Register typescript MIME type if there is none yet.


"use strict";

function getImagesFromDom() {
    return _.chain($("img").toArray())
    .map(function (element) {
        return element.src;
    })
    .value();
}

function endsWith(str, suffix) {
    return str.substr(str.length - suffix.length, str.length) === suffix;
}

var interval = Bacon.fromPoll(3000, function () {
    return "tick";
}).flatMapFirst(function () {
    return Bacon.fromPromise($.get("/photosjson"));
}).onValue(function (json) {
    var existingImages = getImagesFromDom();

    _.chain(json)
    .reverse()
    .each(function (newPic) {
        var exists = existingImages.some(function (url) {
            return endsWith(url, newPic.url);
        });

        if (!exists) {
            $("body").prepend($("<img>").addClass("selfie-image").attr("src", newPic.url));
        }
    });

    // remove when too much.
    _.chain($("img").toArray())
    .drop(1000)
    .each(function (el) {
        el.parentNode.removeChild(el);
    });
});

Use tabs instead of spaces

Use tabs instead of spaces


describe('Check vital page elements on home', () => {
  beforeEach(() => {
    cy.visit('http://localhost:4000/')
  })

  it('Find the header main title', () => {
    cy.get('#header h1').should('have.text', 'frippz.se')
  })

  it('Find main navigation', () => {
    cy.get('#main-nav li').should('have.length', 3)
  })

  it('Find footer', () => {
    cy.get('#footer')
  })

  it('Find footer – external links', () => {
    cy.get('#footer .external-links li').should('have.length', 5)
  })

  it('Find footer – feed links', () => {
    cy.get('#footer .feeds li').should('have.length', 3)
  })
})

describe('Check vital page elements on home', () => {
  beforeEach(() => {
    cy.visit(Cypress.env('TEST_URL'))
  })

  it('Find the header main title', () => {
    cy.get('#header h1').should('have.text', 'frippz.se')
  })

  it('Find main navigation', () => {
    cy.get('#main-nav li').should('have.length', 3)
  })

  it('Find footer', () => {
    cy.get('#footer')
  })

  it('Find footer – external links', () => {
    cy.get('#footer .external-links li').should('have.length', 5)
  })

  it('Find footer – feed links', () => {
    cy.get('#footer .feeds li').should('have.length', 3)
  })
})

Use env var for TEST_URL

Use env var for TEST_URL


/* theme selector
*/

(function() {
  'use strict';

  var themeActive = '';
  var $container = document.querySelector('.themes-box');

  function hashChange () {
    themeActive = window.util.hash('theme');

    // if no theme in the url
    if (!themeActive) {
      // select the first in the list
      $container.querySelector('.theme-item').click();

      return;
    }

    // uncheck previous active theme
    var $themeActive = $container.querySelector('.active');
    if ($themeActive) {
      window.util.removeClass($themeActive, 'active');
    }

    $themeActive = $container.querySelector('.js-' + themeActive);
    window.util.addClass($themeActive, 'active');
  }

  hashChange();
  window.addEventListener('hashchange', hashChange, false);
})();

/* theme selector
*/

(function() {
  'use strict';

  var themeActive = '';
  var $themeActive;
  var $container = document.querySelector('.themes-box');

  function hashChange () {
    themeActive = window.util.hash('theme');

    // if no theme in the url
    if (!themeActive) {
      // select the first in the list
      $container.querySelector('.theme-item').click();

      return;
    }

    // uncheck previous active theme
    $themeActive = $container.querySelector('.active');
    if ($themeActive) {
      window.util.removeClass($themeActive, 'active');
    }

    $themeActive = $container.querySelector('.js-' + themeActive);
    if ($themeActive) {
      window.util.addClass($themeActive, 'active');
    }
  }

  hashChange();
  window.addEventListener('hashchange', hashChange, false);
})();

Check if the dom node for the theme about to be activated exists. To minimize js errors.


Meteor.methods({

  // Tasks
  createTask: function(task) {
    // FIXME: Use collection hooks
    task.createdAt = new Date()
    task.userId = Meteor.userId()

    Tasks.insert(task)
  },

  // Morning Gratitude
  createGratitude: function(gratitude) {
    // FIXME: Use collection hooks
    gratitude.createdAt = new Date()
    gratitude.userId = Meteor.userId()

    Gratitude.insert(gratitude)
  },

  updateGratitude: function(id, newValue) {
    Gratitude.update(id, {
      $set: {"value": newValue}
    })
  },

  // MITs
  createMits: function(mits) {
    // FIXME: Use collection hooks
    mits.createdAt = new Date()
    mits.userId = Meteor.userId()

    Mits.insert(mits)
  },

  updateMit: function(id, newValue) {
    Mits.update(id, {
      $set: {"value": newValue}
    })
  }

});

Meteor.methods({

  // Tasks
  createTask: function(task) {
    // FIXME: Use collection hooks
    task.createdAt = new Date()
    task.userId = Meteor.userId()

    Tasks.insert(task)
  },

  // Morning Gratitude
  createGratitude: function(gratitude) {
    // FIXME: Use collection hooks
    gratitude.createdAt = new Date()
    gratitude.userId = Meteor.userId()

    Gratitude.insert(gratitude)
  },

  updateGratitude: function(id, newValue) {
    Gratitude.update(id, {
      $set: {"value": newValue}
    })
  },

  // MITs
  createMits: function(mits) {
    // FIXME: Use collection hooks
    mits.createdAt = new Date()
    mits.userId = Meteor.userId()

    Mits.insert(mits)
  },

  updateMits: function(id, newValue) {
    Mits.update(id, {
      $set: {"value": newValue}
    })
  }

});

Revert "updateMit" method name change

Revert "updateMit" method name change


// Temporary workaround until wagtail 1.5 is released
$(function() {
    $(".richtext").on("click", function() {
	      var $this = $(this);
	      $(".hallotoolbar .hallojustify button[title]").on("click", function() {
	          var selection = window.getSelection();
	          var block;
            if (selection.baseNode.nodeName === "#text") {
                block = $(selection.baseNode).parents("p");
            } else {
                block = selection.baseNode;
            }
	          var alignment = $(this).attr("title").toLowerCase();
	          $(block).removeClass("center left right justify");
	          $(block).addClass(alignment);
	          $this.trigger("hallomodified", [{content: $this.html()}]);
	          $this.addClass("isModified");
	      });
    });
});

// Temporary workaround until wagtail 1.5 is released
$(function() {
    $(".richtext").on("click", function() {
	      var $this = $(this);
	      $(".hallotoolbar .hallojustify button[title]").on("click", function() {
	          var selection = window.getSelection();
	          var block;
            if (selection.baseNode.nodeName === "#text") {
                block = $(selection.baseNode).parents("p");
            } else {
                block = selection.baseNode;
            }
	          var alignment = $(this).attr("title").toLowerCase();
	          $(block).removeClass("center left right justify");
	          $(block).addClass(alignment);
	          $this.trigger("hallomodified", [{content: $this.html()}]);
	          $this.addClass("isModified");
	      });
    });

    var RTL_LANGUAGES = ["ur"];

    // For RTL languages, change the richtext direction
    $("#id_language").on("change", function() {
        if ($.inArray($(this).val(), RTL_LANGUAGES) !== -1) {
            $(".richtext").attr("dir", "rtl");
        } else {
            $(".richtext").attr("dir", "auto");
        }
    });

    var openedWindows = [];
    window._open = window.open;
    window.open = function(url, name, params) {
        var win = window._open(url, name, params);
        openedWindows.push(win);
        return win;
    }
    $(".action-preview").on("click", function() {
        var $this = $(this);
        // For RTL languages in preview
        if ($.inArray($("#id_language option:selected").val(), RTL_LANGUAGES) !== -1) {
            setTimeout(function() {
                previewWindow = openedWindows.slice(-1)[0];
                if (previewWindow) {
                    var previewDoc = previewWindow.document;
                    $(previewDoc).find("#preview-frame").on("load", function() {
                        $(this).get(0).contentDocument.querySelector("html").setAttribute("dir", "rtl");
                    });
                }
            }, 500);
        }
    });
});

Handle RTL language preview and entry in wagtail admin.


import { combineReducers } from 'redux';
import events from './eventsReducer';
import categories from './categoriesReducer';
import zoom from './zoomReducer';
import mouse from './mouseReducer';
import ui from './uiReducer';

const rootReducer = combineReducers({
	events,
	categories,
	zoom,
	mouse,
	ui,
});

export default rootReducer;

import events from './eventsReducer';
import categories from './categoriesReducer';
import zoom from './zoomReducer';
import mouse from './mouseReducer';
import ui from './uiReducer';

const rootReducer = (state = {}, action = {}) => ({
	events: events(state.events, action),
	categories: categories(state.categories, action),
	zoom: zoom(state.zoom, action),
	mouse: mouse(state.mouse, action),
	ui: ui(state.ui, action),
});

export default rootReducer;

Remove usage of combineReducer in rootReducer


'use strict'

const spawn = require('child_process').spawn

class WrkSpawner {
  constructor (endpoint = 'http://localhost') {
    this.endpoint = endpoint
  }

  run (port) {
    return new Promise((resolve, reject) => {
      this.wrk = spawn('wrk', [ `${this.endpoint}:${port}`, '-d 10', '-c 50', '-t 8' ])

      this.wrk.stdout.on('data', (results) => {
        this.results = results
      })

      this.wrk.on('close', code => code ? reject(code) : resolve(this.results))
      this.wrk.on('error', err => reject(err))
    })
  }

  stop () {
    return new Promise((resolve) => {
      this.wrk.kill()
      resolve()
    })
  }
}

module.exports = WrkSpawner

'use strict'

const spawn = require('child_process').spawn

class WrkSpawner {
  constructor (endpoint = 'http://localhost') {
    this.endpoint = endpoint
  }

  run (port) {
    return new Promise((resolve, reject) => {
      this.wrk = spawn('wrk', [ `${this.endpoint}:${port}`, '-d 5', '-c 50', '-t 8' ])

      this.wrk.stdout.on('data', (results) => {
        this.results = results
      })

      this.wrk.on('close', code => code ? reject(code) : resolve(this.results))
      this.wrk.on('error', err => reject(err))
    })
  }

  stop () {
    return new Promise((resolve) => {
      this.wrk.kill()
      resolve()
    })
  }
}

module.exports = WrkSpawner

Set benchmark to 5 seconds

Set benchmark to 5 seconds


/**
 * Created by dungvn3000 on 3/14/14.
 * The model automatic update mapping field when set associations table.
 */

Ext.define('sunerp.model.BaseModel', {
    extend: 'Ext.data.Model',
    set: function (fieldName, newValue) {
        var me = this;
        me.callParent(arguments);
        if (newValue != null && Ext.isObject(newValue)) {
            Ext.each(me.associations.keys, function (table) {
                if (fieldName == table) {
                    for (var key in newValue) {
                        me.set(table + "." + key, newValue[key]);
                    }
                    me.set(key + 'Id', newValue.id);
                }
            });
        }
    }
});

/**
 * Created by dungvn3000 on 3/14/14.
 * The model automatic update mapping field when set associations table.
 */

Ext.define('sunerp.model.BaseModel', {
    extend: 'Ext.data.Model',
    set: function (fieldName, newValue) {
        var me = this;
        me.callParent(arguments);
        if (newValue != null && Ext.isObject(newValue)) {
            Ext.each(me.associations.keys, function (table) {
                if (fieldName == table) {
                    for (var key in newValue) {
                        me.set(table + "." + key, newValue[key]);
                    }
                    me.set(table + 'Id', newValue.id);
                }
            });
        }
    }
});

Fix inner model id do not automatic set when set a inner model.


import PropTypes from 'prop-types';
import React from 'react';
import { connect } from 'react-redux';
import { Menu } from 'antd';
import { Link } from 'redux-little-router';

const { SubMenu, Item } = Menu;

@connect(
  state => ({
    router: state.router,
  }),
)
export default class NavigationMenu extends React.Component {
  static propTypes = {
    router: PropTypes.object.isRequired,
  };

  render() {
    const { router } = this.props;
    const { pathname, search } = router;

    return (
      <Menu
        defaultOpenKeys={['Recipes', 'Extensions']}
        selectedKeys={[pathname + search]}
        mode="inline"
      >
        <Item key="/"><Link href="/">Home</Link></Item>

        <SubMenu title="Recipes" key="Recipes">
          <Item key="/recipe">
            <Link href="/recipe">View All</Link>
          </Item>
          <Item key="/recipe?hearbeat">
            <Link href="/recipe?hearbeat">Heartbeat</Link>
          </Item>
          <Item key="/recipe?console-log">
            <Link href="/recipe?console-log">console-log</Link>
          </Item>
          <Item key="/recipe?pref-study">
            <Link href="/recipe?pref-study">Preference Study</Link>
          </Item>
        </SubMenu>

        <SubMenu title="Extensions" key="Extensions">
          <Item key="/extension">
            <Link href="/extension">View All</Link>
          </Item>
          <Item key="/extension?pref-study">
            <Link href="/extension?pref-study">Preference Study</Link>
          </Item>
        </SubMenu>
      </Menu>
    );
  }
}

import PropTypes from 'prop-types';
import React from 'react';
import { connect } from 'react-redux';
import { Menu } from 'antd';
import { Link } from 'redux-little-router';

const { SubMenu, Item } = Menu;

@connect(
  state => ({
    router: state.router,
  }),
)
export default class NavigationMenu extends React.Component {
  static propTypes = {
    router: PropTypes.object.isRequired,
  };

  render() {
    const { router } = this.props;
    const { pathname, search } = router;

    return (
      <Menu
        defaultOpenKeys={['Recipes', 'Extensions']}
        selectedKeys={[pathname + search]}
        mode="inline"
      >
        <Item key="/"><Link href="/">Home</Link></Item>

        <SubMenu title="Recipes" key="Recipes">
          <Item key="/recipe">
            <Link href="/recipe">View All</Link>
          </Item>
        </SubMenu>

        <SubMenu title="Extensions" key="Extensions">
          <Item key="/extension">
            <Link href="/extension">View All</Link>
          </Item>
        </SubMenu>
      </Menu>
    );
  }
}

Remove extra nav menu items

Remove extra nav menu items


exports.config = {
    capabilities: {'browserName': 'chrome'},
    /*multiCapabilities: [
        {'browserName': 'chrome'},
        {'browserName': 'firefox'},
        {'browserName': 'opera'},
        {'browserName': 'safari'}
    ],*/
    framework: 'jasmine2',
    jasmineNodeOpts: {
        showColors: true
    },
    seleniumPort: 4840,
    onPrepare: function() {
        browser.driver.manage().window().setSize(1440, 800);
        browser.get('http://localhost:8000/#');
    },
    suites: {
        login:  './login/*.test.js',
        header: './common/*.test.js',
        case:   './cases/*.test.js',
        users:  './users/*.test.js'
    }
};

exports.config = {
    capabilities: {'browserName': 'chrome'},
    /*multiCapabilities: [
        {'browserName': 'chrome'},
        {'browserName': 'firefox'},
        {'browserName': 'opera'},
        {'browserName': 'safari'}
    ],*/
    framework: 'jasmine2',
    jasmineNodeOpts: {
        showColors: true
    },
    seleniumPort: 4840,

    /* Due to issues with slow Selenium startup due to RNG, see http://stackoverflow.com/questions/14058111/selenium-server-doesnt-bind-to-socket-after-being-killed-with-sigterm. */
    seleniumArgs: ["-Djava.security.egd=file:/dev/./urandom"],

    onPrepare: function() {
        browser.driver.manage().window().setSize(1440, 800);
        browser.get('http://localhost:8000/#');
    },
    suites: {
        login:  './login/*.test.js',
        header: './common/*.test.js',
        case:   './cases/*.test.js',
        users:  './users/*.test.js'
    }
};

Add option that fixes slow Selenium webdriver startup on systems with low entropy (e.g. VMs).


function Cell(row, col, value, board) {
  this.row = row,
  this.col = col,
  this.value = value,
  this.board = board
}

Cell.prototype.find = function(word, visited=new Set(), wordIndex=0) {
  if(visited.has(this)) return null;
  word = word.toUpperCase();
  var die = this.value;
  var letter = word[wordIndex];

  if(wordIndex === word.length - 1) {
    if(die !== letter) return null;
    visited.add(this);
    return visited;
  }

  if(die === letter) {
    visited.add(this);
    var neighbors = this.getNeighbors(this.row, this.col);
    var pass = neighbors.some(function(cell, index) {
      return cell.find(word, visited, wordIndex + 1)
    }, this);
    if(pass) return visited;
  }
  visited.delete(this);
  return null;
};

Cell.prototype.getNeighbors = function() {
  var neighbors = [];
  var i = -1;
  while(i <= 1) {
    var j = -1;
    var row = this.row + i;
    while(j <= 1) {
      var col = this.col + j++;
      var neighbor = this.board.cell(row, col);
      if(neighbor) neighbors.push(neighbor);
    }
    i++;
  }
  return neighbors;
};

module.exports = Cell;

function Cell(row, col, value, board) {
  this.row = row,
  this.col = col,
  this.value = value,
  this.board = board
}

Cell.prototype.find = function(word, visited=new Set(), wordIndex=0) {
  if(visited.has(this)) return null;
  word = word.toUpperCase();
  var die = this.value;
  var letter = word[wordIndex];

  if(wordIndex === word.length - 1) {
    if(die !== letter) return null;
    visited.add(this);
    return visited;
  }

  if(die === letter) {
    visited.add(this);
    var neighbors = this.getNeighbors(this.row, this.col);
    var pass = neighbors.some(function(cell, index) {
      return cell.find(word, visited, wordIndex + 1)
    }, this);
    if(pass) return visited;
  }
  visited.delete(this);
  return null;
};

Cell.prototype.getNeighbors = function() {
  if(this.neighbors) return this.neighbors;
  this.neighbors = [];
  var i = -1;
  while(i <= 1) {
    var j = -1;
    var row = this.row + i;
    while(j <= 1) {
      var col = this.col + j++;
      var neighbor = this.board.cell(row, col);
      if(neighbor) this.neighbors.push(neighbor);
    }
    i++;
  }
  return this.neighbors;
};

module.exports = Cell;

Add caching for get neighbors method

Add caching for get neighbors method


/**
 * @license
 * Copyright 2021 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @fileoverview Common configuration for Gulp scripts.
 */

var path = require('path');

// Paths are all relative to the repository root.  Do not include
// trailing slash.
module.exports = {
  // Directory to write compiled output to.
  BUILD_DIR: 'built',

  // Directory in which to assemble (and from which to publish) the
  // blockly npm package.
  RELEASE_DIR: 'dist',
};

/**
 * @license
 * Copyright 2021 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @fileoverview Common configuration for Gulp scripts.
 */

var path = require('path');

// Paths are all relative to the repository root.  Do not include
// trailing slash.
module.exports = {
  // Directory to write compiled output to.
  BUILD_DIR: 'build',

  // Directory in which to assemble (and from which to publish) the
  // blockly npm package.
  RELEASE_DIR: 'dist',
};

Rename BUILD_DIR to "build" (was "built")

Per discussion in PR #4968, the dirname "build" is widely used for
this purpose.


/* jshint: node:true */
module.exports = function (grunt) {
    'use strict';

    grunt.initConfig({
        phplint: {
            application: ["lib/*.php", "tests/**/*.php"]
        },
        phpcs: {
            application: {
                src: 'lib/*.php'
            },
            options: {
                standard: 'PSR1',
                showSniffCodes: true,
                verbose: true
            }
        },
        phpunit: {
            application: {
                dir: 'tests/'
            },
            options: {
                colors: true,
                coverage: true
            }
        },
        phpcpd: {
            application: {
                dir: 'lib/'
            },
            options: {
                quiet: true
            }
        },
        phpmd: {
            application: {
                dir: 'lib/'
            },
            options: {
                rulesets: 'codesize,unusedcode,naming,design'
            }
        }
    });

    grunt.loadNpmTasks('grunt-phpcs');
    grunt.loadNpmTasks('grunt-phplint');
    grunt.loadNpmTasks('grunt-phpunit');
    grunt.loadNpmTasks('grunt-phpcpd');
    grunt.loadNpmTasks('grunt-phpmd');

    grunt.registerTask("default", [
        "phplint",
        "phpunit",
        "phpcpd"
    ]);
};

/* jshint: node:true */
module.exports = function (grunt) {
    'use strict';

    grunt.initConfig({
        phplint: {
            application: ["lib/*.php", "tests/**/*.php"]
        },
        phpcs: {
            application: {
                src: 'lib/*.php'
            },
            options: {
                standard: 'PSR1',
                showSniffCodes: true,
                verbose: true
            }
        },
        phpunit: {
            application: {
                dir: 'tests/'
            },
            options: {
                colors: true,
                coverage: true
            }
        },
        phpcpd: {
            application: {
                dir: 'lib/'
            },
            options: {
                quiet: true
            }
        },
        phpmd: {
            application: {
                dir: 'lib/'
            },
            options: {
                rulesets: 'codesize,unusedcode,naming,design'
            }
        }
    });

    grunt.loadNpmTasks('grunt-phpcs');
    grunt.loadNpmTasks('grunt-phplint');
    grunt.loadNpmTasks('grunt-phpunit');
    grunt.loadNpmTasks('grunt-phpcpd');
    grunt.loadNpmTasks('grunt-phpmd');

    grunt.registerTask("default", [
        "phplint",
        "phpunit",
        "phpcpd"
    ]);

    grunt.registerTask('travis', [
        "phplint",
        "phpunit",
        "phpcpd"
    ]);
};

Add task for Travis CI

Add task for Travis CI


var http = require('http')
, async = require('async')
, fs = require('fs');

async.waterfall([
  function(cb) {
    cb(null, process.argv[2]);
  },
  function(fileFullPath, cb) {
    fs.readFile(fileFullPath, 'utf8', cb);
  },
  function(url, cb) {
    var body = '';
    http.get(url, function(res){
      res.on('data', function(chunk){
        body += chunk.toString();
      });
      res.on('end', function(){
        cb(null, body);
      });
    }).on('error', function(err) {
      cb(err);
    });
  }
], function(err, result) {
  if (err)
    return console.error(err);

  console.log(result);
});

Complete 'Waterfall' challenge in 'Async you' tutorial


const path = require('path');
const nodeExternals = require('webpack-node-externals');

module.exports = {
  entry: './src/index.js',
  target: 'node',
  externals: [nodeExternals()],
  output: {
    path: path.join(__dirname, '/lib'),
    filename: 'index.js',
    library: 'graphql-accounts',
    libraryTarget: 'umd',
  },
  modulesDirectories: [
    'src',
    'node_modules',
  ],
  module: {
    loaders: [
      {
        test: /\.js$/,
        loader: 'babel',
        exclude: /node_modules/,
      }
    ],
  },
};

const path = require('path');
const nodeExternals = require('webpack-node-externals');

module.exports = {
  entry: './src/index.js',
  target: 'node',
  externals: [nodeExternals()],
  output: {
    path: path.join(__dirname, '/lib'),
    filename: 'index.js',
    library: 'react-accounts',
    libraryTarget: 'umd',
  },
  modulesDirectories: [
    'src',
    'node_modules',
  ],
  module: {
    loaders: [
      {
        test: /\.js$/,
        loader: 'babel',
        exclude: /node_modules/,
      }
    ],
  },
};

Fix correct library name in webpack

Fix correct library name in webpack


var path = require('path');
var webpack = require('webpack');

var minify = new webpack.optimize.UglifyJsPlugin({
	minimize: true, compress: { warnings: false }, output: {
    semicolons: false,
    indent_level: 0
  }
});

var loaders = [{
	"test": /\.imba$/,
	"loader": path.join(__dirname, "./loader")
}];

module.exports = [{
	module: {loaders: loaders},
	resolve: {extensions: ['*', '.imba', '.js']},
	entry: "./src/imba/index.imba",
	output: { filename: "./imba.js"},
	node: {fs: false, process: false, global: false},
	plugins: [minify]
},{
	module: {loaders: loaders},
	resolve: {extensions: ['*', '.imba', '.js']},
	entry: "./test/index.imba",
	output: { filename: "./test/client.js"},
	node: {fs: false, process: false, global: false}
}]

var path = require('path');
var webpack = require('webpack');

var minify = new webpack.optimize.UglifyJsPlugin({
	minimize: true, compress: { warnings: false }, output: {
    semicolons: false,
    indent_level: 0
  }
});

var loaders = [{
	"test": /\.imba$/,
	"loader": path.join(__dirname, "./loader")
}];

module.exports = [{
	module: {loaders: loaders},
	resolve: {extensions: ['*', '.imba', '.js']},
	entry: "./src/imba/index.imba",
	output: {
		filename: "./imba.js",
		library: {
			root: "Imba",
			amd: "imba",
			commonjs: "imba"
		},
		libraryTarget: "umd"
	},
	node: {fs: false, process: false, global: false}
	// plugins: [minify]
},{
	module: {loaders: loaders},
	resolve: {extensions: ['*', '.imba', '.js']},
	entry: "./test/index.imba",
	output: { filename: "./test/client.js"},
	node: {fs: false, process: false, global: false}
}]

Make build process output umd bundle

Make build process output umd bundle


$(function() {
    $('.atml-description--title').click(function(e) {

        e.preventDefault();

        var $this = $(this);
        var $contentId = $this.data('content');
        var $content = $this.parent().next('div');
        var $descriptions = $('.atml-description--content');
        var $listItems = $('.atml-description--list-item')
        var $selectedListWrapper = $this.closest('li');
        var $images = $('.atml-image--image');
        var $relatedImage = $('.atml-image--image[data-content="' + $contentId + '"]');
        var $title = $('.atml-header h1');

        $descriptions.removeClass('active')
        $images.removeClass('active')
        $content.addClass('active')
        $relatedImage.addClass('active');

        $listItems.removeClass('active');
        $selectedListWrapper.addClass('active');

        console.log('Content ID', $contentId);
        switch ($contentId) {
            case 4:
            case 5:
            case 6:
            case 7:
                $title.text('Loss Pathways');
                break;
            case 1:
            case 8:
            case 2:
            case 3:
            default:
                $title.text('Atmospheric Lifetimes');
                break;
        }

        return false;
    });
});
26  115 164

Remove random numbers at bottom

Remove random numbers at bottom


// import Admin class
import Admin from '../module/Admin';

module.exports = {
  // Add a new book to database
  createBook(req, res) {
    new Admin().addBook(
      req.body.title,
      req.body.author,
      req.body.description,
      req.body.imageURL,
      req.body.subject,
      req.body.quantity,
    )
      .then(result => res.status(201).send(result))
      .catch(error => res.status(400).send(error));
  },
};

// import Admin class
import Admin from '../module/Admin';

module.exports = {
  // Add a new book to database
  createBook(req, res) {
    // Check the role of user from decoded token
    // If 'User' return error else add book
    if (req.decoded.role !== 'Admin') return res.status(401).send({ msg: 'Unauthorized user' });
    new Admin().addBook(
      req.body.title,
      req.body.author,
      req.body.description,
      req.body.imageURL,
      req.body.subject,
      req.body.quantity,
    )
      .then(result => res.status(201).send(result))
      .catch(error => res.status(500).send(error));
  },
};

Allow only authenticated admin to add a book


'use strict'

var fs = require('fs'),
    words = require('./');

fs.writeFileSync('Supported-words.md',
    'Supported Words:\n' +
    '=================\n' +
    '\n' +
    '| word | polarity | valence |\n' +
    '|:----:|:--------:|:-------:|\n' +

    Object.keys(words).map(function (word) {
        var valence = words[word];

        return '| ' + [
            word,
            valence > 0 ? ':smile:' : ':frowning:',
            valence > 0 ? '+' + valence : valence
        ].join(' | ') + ' |';
    }).join('\n') +

    '\n'
);

Add strict mode to support generation script


var PLAYER = {
  points: 0
}

function earnPoints() {
  PLAYER.points += 1
}

$(document).ready(function() {
  $('#popcornkernel').click(earnPoints);
})

var PLAYER = {
  points: 0
}

function earnPoints() {
  PLAYER.points += 1
  updateUI()
}

function updateUI() {
  $('#points').text(PLAYER.points)
}

$(document).ready(function() {
  $('#popcornkernel').click(earnPoints);
})

Update points in UI after every points update


import _ from 'lodash'

import renderStaticStyles from '../core/renderer'

export default {
  create(styles) {
    // flat style object without selectors
    const firstKey = styles[Object.keys(styles)[0]]
    if (!_.isPlainObject(firstKey) && !_.isFunction(firstKey)) {
      return generateScopedStyles(styles)
    }

    return Object.keys(styles).reduce((selectors, selector) => {
      selectors[selector] = generateScopedStyles(styles[selector])
      return selectors
    }, { })
  },

  combineStyles(...styles) {
    return styles.reduce((combined, single) => {
      const { _selector, ...otherStyles } = single

      if (_selector) {
        combined._selector = (combined._selector ? combined._selector : []).concat(_selector)
      }
      return _.merge(combined, otherStyles)
    }, { })
  }
}

export function generateScopedStyles(styles) {
  const selector = renderStaticStyles(styles)

  if (selector) {
    return {
      ...styles,
      _selector: [ selector ]
    }
  }
  return styles
}

import _ from 'lodash'
import renderStaticStyles from '../core/renderer'
import { PropTypes } from 'react'
import TextStylePropTypes from 'react-native/Libraries/Text/TextStylePropTypes'
import ViewStylePropTypes from 'react-native/Libraries/Components/View/ViewStylePropTypes'
import StyleSheetValidation from 'react-native/Libraries/StyleSheet/StyleSheetValidation'

TextStylePropTypes._selector = PropTypes.array
ViewStylePropTypes._selector = PropTypes.array

StyleSheetValidation.addValidStylePropTypes({
  _selector: PropTypes.array
})

export default {
  create(styles) {
    // flat style object without selectors
    const firstKey = styles[Object.keys(styles)[0]]
    if (!_.isPlainObject(firstKey) && !_.isFunction(firstKey)) {
      return generateScopedStyles(styles)
    }

    return Object.keys(styles).reduce((selectors, selector) => {
      selectors[selector] = generateScopedStyles(styles[selector])
      return selectors
    }, { })
  },

  combineStyles(...styles) {
    return styles.reduce((combined, single) => {
      const { _selector, ...otherStyles } = single

      if (_selector) {
        combined._selector = (combined._selector ? combined._selector : []).concat(_selector)
      }
      return _.merge(combined, otherStyles)
    }, { })
  }
}

export function generateScopedStyles(styles) {
  const selector = renderStaticStyles(styles)

  if (selector) {
    return {
      ...styles,
      _selector: [ selector ]
    }
  }
  return styles
}

Add _selector to React Native's style propTypes to fix debugger warnings