import Ember from 'ember';
import MultiStatsSocket from 'ui/utils/multi-stats';

export default Ember.Route.extend({
  statsSocket: null,

  model() {
    return this.modelFor('service').get('service');
  },

  activate() {
    var stats = MultiStatsSocket.create({
      resource: this.modelFor('service').get('service'),
      linkName: 'containerStats',
    });

    this.set('statsSocket',stats);
    stats.on('dataPoint', (data) => {
      var controller = this.get('controller');
      if ( controller )
      {
        controller.onDataPoint(data);
      }
    });
  },

  deactivate() {
    this.get('statsSocket').close();
  }
});

import Ember from 'ember';
import MultiStatsSocket from 'ui/utils/multi-stats';

export default Ember.Route.extend({
  statsSocket: null,

  model() {
    var promises = [];

    // Load the hosts for the instances if they're not already there
    var service = this.modelFor('service').get('service');
    service.get('instances').forEach((instance) => {
      if ( !instance.get('primaryHost') )
      {
        promises.push(instance.importLink('hosts'));
      }
    });

    return Ember.RSVP.all(promises).then(() => {
      return service;
    });
  },

  activate() {
    var stats = MultiStatsSocket.create({
      resource: this.modelFor('service').get('service'),
      linkName: 'containerStats',
    });

    this.set('statsSocket',stats);
    stats.on('dataPoint', (data) => {
      var controller = this.get('controller');
      if ( controller )
      {
        controller.onDataPoint(data);
      }
    });
  },

  deactivate() {
    this.get('statsSocket').close();
  }
});

Fix unknown host on services


import React from 'react'
import PropTypes from 'prop-types'
import { Provider } from 'react-redux'
import { BrowserRouter } from 'react-router-dom'
import ApolloClient from 'apollo-boost';
import { ApolloProvider } from 'react-apollo';
import BaseDataView from './BaseDataView'

function getCookieValue(a) {
  var b = document.cookie.match('(^|;)\\s*' + a + '\\s*=\\s*([^;]+)');
  return b ? b.pop() : '';
}

const client = new ApolloClient({
  uri: process.env.REACT_APP_GRAPHQL_API_URL + 'graphql',
  headers: {
    'Authorization': 'Bearer ' + getCookieValue('token')
  }
  // options: { mode: 'no-cors' }
});
window.apiUrl = process.env.REACT_APP_FLASK_API_URL;

const Root = ({ store }) => (
  <ApolloProvider client={client}>
    <Provider store={store}>
      <BrowserRouter>
        <BaseDataView />
      </BrowserRouter>
    </Provider>
  </ApolloProvider>
)

Root.propTypes = {
  store: PropTypes.object.isRequired
}

export default Root

import React from 'react'
import PropTypes from 'prop-types'
import { Provider } from 'react-redux'
import { BrowserRouter } from 'react-router-dom'
import ApolloClient from 'apollo-boost';
import { ApolloProvider } from 'react-apollo';
import BaseDataView from './BaseDataView'

function getCookieValue(a) {
  var b = document.cookie.match('(^|;)\\s*' + a + '\\s*=\\s*([^;]+)');
  return b ? b.pop() : '';
}

const client = new ApolloClient({
  uri: process.env.REACT_APP_FLASK_API_URL + 'graphql',
  headers: {
    'Authorization': 'Bearer ' + getCookieValue('token')
  }
  // options: { mode: 'no-cors' }
});
window.apiUrl = process.env.REACT_APP_FLASK_API_URL;

const Root = ({ store }) => (
  <ApolloProvider client={client}>
    <Provider store={store}>
      <BrowserRouter>
        <BaseDataView />
      </BrowserRouter>
    </Provider>
  </ApolloProvider>
)

Root.propTypes = {
  store: PropTypes.object.isRequired
}

export default Root

Use correct uri for AppolloClient


'use strict';

// Declare app level module which depends on filters, and services

var app = angular.module( 'myApp', [
    'ngRoute',

    'myApp.controllers',
    'myApp.filters',
    'myApp.services',
    'myApp.directives',

    // 3rd party dependencies
    'btford.socket-io'
] );

app.config( function ( $routeProvider, $locationProvider ) {
    $routeProvider.
    when( '/home', {
        templateUrl: 'partials/home',
        controller: 'HomeCtrl'
    } ).
    when( '/artist/:artistURI', {
        templateUrl: 'partials/artist',
        controller: 'ArtistCtrl'
    } ).
    when( '/album/:albumURI', {
        templateUrl: 'partials/album',
        controller: 'AlbumCtrl'
    } ).
    otherwise( {
        redirectTo: '/home'
    } );

    $locationProvider.html5Mode( true );
});

'use strict';

// Declare app level module which depends on filters, and services

var app = angular.module( 'myApp', [
    'ngRoute',

    'myApp.controllers',
    'myApp.filters',
    'myApp.services',
    'myApp.directives',

    // 3rd party dependencies
    'btford.socket-io',
    'fully-loaded'
] );

app.config( function ( $routeProvider, $locationProvider ) {
    $routeProvider.
    when( '/home', {
        templateUrl: 'partials/home',
        controller: 'HomeCtrl'
    } ).
    when( '/artist/:artistURI', {
        templateUrl: 'partials/artist',
        controller: 'ArtistCtrl'
    } ).
    when( '/album/:albumURI', {
        templateUrl: 'partials/album',
        controller: 'AlbumCtrl'
    } ).
    otherwise( {
        redirectTo: '/home'
    } );

    $locationProvider.html5Mode( true );
});

Install this the right way


import React, { PropTypes } from 'react';

const App = ({ children }) => (
  <div className="page-container">
    {children}
  </div>
);

App.propTypes = {
  children: PropTypes.element.isRequired
};

export default App;

import React, { PropTypes } from 'react';

const App = (props) => (
  <div className="page-container">
    {React.cloneElement({...props}.children, {...props})}
  </div>
);

App.propTypes = {
  children: PropTypes.element.isRequired
};

export default App;

Fix warning messages regarding refs and key


const fs = require('fs');
const path = require('path');

/**
 * Finds package.json file from either the directory the script was called from or a supplied path.
 *
 *     console.log(findPackageFileInPath());
 *     console.log(findPackageFileInPath('./package.json'));
 *     console.log(findPackageFileInPath('~/git/github/doxdox/'));
 *
 * @param {String} [input] Directory or file.
 * @return {String} Path to package.json file.
 * @public
 */

module.exports.findPackageFileInPath = input => {

    if (!input) {

        input = process.cwd();

    }

    if (fs.existsSync(input)) {

        const stat = fs.statSync(input);

        if (stat.isDirectory()) {

            return path.resolve(path.join(input, '/package.json'));

        } else if (stat.isFile()) {

            return path.resolve(path.join(path.dirname(input), '/package.json'));

        }

    }

    return null;

};

const fs = require('fs');
const path = require('path');

/**
 * Finds package.json file from either the directory the script was called from or a supplied path.
 *
 *     console.log(findPackageFileInPath());
 *     console.log(findPackageFileInPath('./package.json'));
 *     console.log(findPackageFileInPath('~/git/github/doxdox/'));
 *
 * @param {String} [input] Directory or file.
 * @return {String} Path to package.json file.
 * @public
 */

const findPackageFileInPath = input => {

    if (!input) {

        input = process.cwd();

    }

    if (fs.existsSync(input)) {

        const stat = fs.statSync(input);

        if (stat.isDirectory()) {

            return path.resolve(path.join(input, '/package.json'));

        } else if (stat.isFile()) {

            return path.resolve(path.join(path.dirname(input), '/package.json'));

        }

    }

    return null;

};

module.exports = {
    findPackageFileInPath
};

Change how method is exported.


'use babel'

/* @flow */

import Path from 'path'
import {findCached} from './common'
import {CONFIG_FILE_NAME} from '../defaults'

export function findRoot(directory: string): string {
  const configFile = findCached(directory, CONFIG_FILE_NAME)
  if (configFile !== null) {
    return Path.dirname(String(configFile))
  } else return directory
}

'use babel'

/* @flow */

import Path from 'path'
import {findCached} from './common'
import {CONFIG_FILE_NAME} from '../defaults'

export async function findRoot(directory: string): Promise<string> {
  const configFile = await findCached(directory, CONFIG_FILE_NAME)
  if (configFile) {
    return Path.dirname(configFile)
  } else return directory
}

Upgrade findRoot to be async


/*!
 * Generator-Gnar Gruntfile
 * http://gnarmedia.com
 * @author Adam Murphy
 */

// Generated on <%= (new Date).toISOString().split('T')[0] %> using <%= pkg.name %> <%= pkg.version %>

'use strict';

/**
 * Grunt module
 */
module.exports = function (grunt) {

  /**
   * Generator-Gnar Grunt config
   */
  grunt.initConfig({

    pkg: grunt.file.readJSON('package.json'),

    /**
     * Set project info
     */
    project: {
      src: 'src',
      dist: 'dist',
      assets: '<%= project.dist %>/assets',
      css: [
        '<%= project.src %>/scss/style.scss'
      ],
      js: [
        '<%= project.src %>/js/*.js'
      ]
    }

  });

  /**
   * Default task
   * Run `grunt` on the command line
   */
  grunt.registerTask('default', [
  ]);

};

/*!
 * Generator-Gnar Gruntfile
 * http://gnarmedia.com
 * @author Adam Murphy
 */

// Generated on <%= (new Date).toISOString().split('T')[0] %> using <%= pkg.name %> <%= pkg.version %>

'use strict';

/**
 * Grunt module
 */
module.exports = function (grunt) {

  /**
   * Generator-Gnar Grunt config
   */
  grunt.initConfig({

    pkg: grunt.file.readJSON('package.json'),

    /**
     * Set project info
     */
    project: {
      src: 'src',
      dist: 'dist',
      assets: '<%= project.dist %>/assets',
      css: [
        '<%= project.src %>/scss/style.scss'
      ],
      js: [
        '<%= project.src %>/js/*.js'
      ],
      html: [
        '<%= project.src %>/html/*.html'
      ]
    }

  });

  /**
   * Default task
   * Run `grunt` on the command line
   */
  grunt.registerTask('default', [
  ]);

};

Add html property to project object


const path = require('path');

const HtmlWebPackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = {
  entry: {
    index: ['./src/index.css', './src/index.js']
  },
  node: false,
  output: {
    path: path.join(__dirname, 'dist'),
    chunkFilename: 'chunks/[id].js',
    clean: true
  },
  devServer: {
    static: './dist',
    compress: true,
    port: 3001,
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        enforce: "pre",
        use: ["source-map-loader"],
      },
      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader'
        ]
      },
    ]
  },
  plugins: [
    new HtmlWebPackPlugin({
      title: 'ArcGIS API  for JavaScript',
      template: './public/index.html',
      filename: './index.html',
      chunksSortMode: 'none',
      inlineSource: '.(css)$'
    }),
    new MiniCssExtractPlugin({
      filename: "[name].[chunkhash].css",
      chunkFilename: "[id].css"
    })
  ]
};

const path = require('path');

const HtmlWebPackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = {
  entry: {
    index: ['./src/index.css', './src/index.js']
  },
  node: false,
  output: {
    path: path.join(__dirname, 'dist'),
    chunkFilename: 'chunks/[id].js'
  },
  devServer: {
    static: path.join(__dirname, 'dist'),
    compress: true,
    port: 3001,
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        enforce: "pre",
        use: ["source-map-loader"],
      },
      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader'
        ]
      },
    ]
  },
  plugins: [
    new HtmlWebPackPlugin({
      title: 'ArcGIS API  for JavaScript',
      template: './public/index.html',
      filename: './index.html',
      chunksSortMode: 'none',
      inlineSource: '.(css)$'
    }),
    new MiniCssExtractPlugin({
      filename: "[name].[chunkhash].css",
      chunkFilename: "[id].css"
    })
  ]
};

Update webpack static, remove webpack clean so dist builds work again.


import {Hooks} from 'PluginSDK';

let EnvironmentVariables = {
  description: 'Set environment variables for each task your service launches in addition to those set by Mesos.',
  type: 'object',
  title: 'Environment Variables',
  properties: {
    variables: {
      type: 'array',
      duplicable: true,
      addLabel: 'Add Environment Variable',
      getter: function (service) {
        let variableMap = service.getEnvironmentVariables();
        if (variableMap == null) {
          return [{}];
        }
        return Object.keys(variableMap).map(function (key) {
          return Hooks.applyFilter('variablesGetter', {
            key,
            value: variableMap[key]
          }, service);
        });
      },
      itemShape: {
        properties: {
          key: {
            title: 'Key',
            type: 'string'
          },
          value: {
            title: 'Value',
            type: 'string'
          }
        }
      }
    }
  }
};

module.exports = EnvironmentVariables;

import {Hooks} from 'PluginSDK';

let EnvironmentVariables = {
  description: 'Set environment variables for each task your service launches in addition to those set by Mesos.',
  type: 'object',
  title: 'Environment Variables',
  properties: {
    variables: {
      type: 'array',
      duplicable: true,
      addLabel: 'Add Environment Variable',
      getter: function (service) {
        let variableMap = service.getEnvironmentVariables();
        if (variableMap == null) {
          return [];
        }
        return Object.keys(variableMap).map(function (key) {
          return Hooks.applyFilter('variablesGetter', {
            key,
            value: variableMap[key]
          }, service);
        });
      },
      itemShape: {
        properties: {
          key: {
            title: 'Key',
            type: 'string'
          },
          value: {
            title: 'Value',
            type: 'string'
          }
        }
      }
    }
  }
};

module.exports = EnvironmentVariables;

Remove empty object from variables array


"use strict";

const Joi = require('joi');

const contactSchema = require('./contactSchema');
const licenseSchema = require('./licenseSchema');
const pathsSchema = require('./pathsSchema');

module.exports = Joi.object({
  'title': Joi.string()
    .required(),
  'description': Joi.string(),
  'termsOfService': Joi.string(),
  'contact': contactSchema.required(),
  'license': licenseSchema,
  'paths': pathsSchema
});

"use strict";

const Joi = require('joi');

const contactSchema = require('./contactSchema');
const licenseSchema = require('./licenseSchema');
const pathsSchema = require('./pathsSchema');

module.exports = Joi.object({
  'title': Joi.string()
    .required(),
  'description': Joi.string(),
  'termsOfService': Joi.string(),
  'contact': contactSchema.required(),
  'license': licenseSchema,
  'paths': pathsSchema,
  'permissionProvider': Joi.func()
});

Add permission provider to the info schema


import Vue from 'vue'
import App from './App'
import I18n from 'vue-i18n'
import Cookie from 'vue-cookie'
import { sync } from 'vuex-router-sync'
import router from './router'
import store from './store'
import en from 'locales/en'
import ja from 'locales/ja'

if (process.env.NODE_ENV === 'production') {
  // Enable Progressive Web App
  require('./pwa')

  // Redirect HTTP to HTTPS
  if (window.location.protocol === 'http:') {
    window.location.protocol = 'https'
  }
}

sync(store, router)

const locales = {
  en: en,
  ja: ja
}

Vue.use(Cookie)
Vue.use(I18n)

// Check if language cookie has been set
// If so, use it
// Else use English
Vue.config.lang = Vue.cookie.get('lang') ? Vue.cookie.get('lang') : 'en'

// Set fallback used for untranslated strings
Vue.config.fallbackLang = 'en'

Object.keys(locales).forEach(lang => {
  Vue.locale(lang, locales[lang])
})

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  store,
  render: h => h(App)
})

import Vue from 'vue'
import App from './App'
import I18n from 'vue-i18n'
import Cookie from 'vue-cookie'
import { sync } from 'vuex-router-sync'
import router from './router'
import store from './store'
import en from 'locales/en'
import ja from 'locales/ja'

if (process.env.NODE_ENV === 'production') {
  // Enable Progressive Web App
  require('./pwa')

  // Redirect HTTP to HTTPS
  // if (window.location.protocol === 'http:') {
  //   window.location.protocol = 'https'
  // }
}

sync(store, router)

const locales = {
  en: en,
  ja: ja
}

Vue.use(Cookie)
Vue.use(I18n)

// Check if language cookie has been set
// If so, use it
// Else use English
Vue.config.lang = Vue.cookie.get('lang') ? Vue.cookie.get('lang') : 'en'

// Set fallback used for untranslated strings
Vue.config.fallbackLang = 'en'

Object.keys(locales).forEach(lang => {
  Vue.locale(lang, locales[lang])
})

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  store,
  render: h => h(App)
})

Use Cloudflare for HTTPS redirect


// Main JavaScript Document
var cards = [];
var mainContent = document.getElementId('main-content');
var mainContentRegion = new ZingTouch.Region(mainContent);

//Create the card object template
function contentCard(title, img, desc, link) {
		"use strict";
		this.title = title;
		this.img   = img;
		this.desc  = desc;
		this.link  = link;
}

var cardPicker = function() {

};

// Main JavaScript Document
var cards = [];
var mainContent = document.getElementId('main-content');
var mainContentRegion = new ZingTouch.Region(mainContent);

//Create the card object template
function contentCard(title, img, desc, link) {
	"use strict";
	this.title = title;
	this.img   = img;
	this.desc  = desc;
	this.link  = link;
}

var cardPicker = (function() {
	"use strict";
	var cards = [];
	var index = 0;

	function next() {
		index = index++ < cards.length ? index : 0;
		return cards(index);
	}

	function current() {
		return cards(index);
	}

	return {
		next: next,
		current: current
	};

})();

Create Card Object template and Card Picker


import Noty from "noty";
import "noty/src/noty.scss";
import "noty/src/themes/sunset.scss";

export function showNotification({ text, type = "success" }) {
  return new Noty({
    theme: "sunset",
    timeout: 3500, // [integer|boolean] delay for closing event in milliseconds. Set false for sticky notifications
    layout: "bottomRight",
    progressBar: true,
    type,
    text,
    animation: {
      open: "animated fadeInUp",
      close: "animated fadeOutDown"
    }
  }).show();
}

// TODO: Remove this after all notification usages are through a webpack bundle.
window.notifications = (function() {
  return { show: showNotification };
})();

import Noty from "noty";
import "noty/src/noty.scss";
import "noty/src/themes/relax.scss";

export function showNotification({ text, type = "success" }) {
  return new Noty({
    theme: "relax",
    timeout: 3500, // [integer|boolean] delay for closing event in milliseconds. Set false for sticky notifications
    layout: "bottomRight",
    progressBar: true,
    type,
    text,
    animation: {
      open: "animated fadeInUp",
      close: "animated fadeOutDown"
    }
  }).show();
}

// TODO: Remove this after all notification usages are through a webpack bundle.
window.notifications = (function() {
  return { show: showNotification };
})();

Update to use relax theme


var validateFloat = function(val) {
    if (typeof val != 'undefined') {
        val = parseFloat(val);
        if (! isNaN(val)) {
            if (val > -99999 ) {
                return val;
            }
        }
    }
    return Infinity;
};

var enableFilter = function (id) {
    var f = window.app.filters.get(id);

    // FIXME: data keys are assumed to be lower case, but this is not checked/ensured
    var id_lower = id.toLowerCase();

    f._dx = window.app.crossfilter.dimension( function(d) {return validateFloat(d[id_lower]);});
    f.active = true;
};

var disableFilter = function (id) {
    var f = window.app.filters.get(id);

    f._dx.dispose();
    delete f._dx;
};

module.exports = {
    validateFloat: validateFloat,
    enableFilter: enableFilter,
    disableFilter: disableFilter,
};

var validateFloat = function(val) {
    if (typeof val != 'undefined') {
        val = parseFloat(val);
        if (! isNaN(val)) {
            if (val > -99999 ) {
                return val;
            }
        }
    }
    return Infinity;
};

var enableFilter = function (id) {
    var f = window.app.filters.get(id);

    // FIXME: data keys are assumed to be lower case, but this is not checked/ensured
    var id_lower = id.toLowerCase();

    f._dx = window.app.crossfilter.dimension( function(d) {return validateFloat(d[id_lower]);});
    f.active = true;
};

var disableFilter = function (id) {
    var f = window.app.filters.get(id);

    f._dx.dispose();
    delete f._dx;

    f.active = false;
};

module.exports = {
    validateFloat: validateFloat,
    enableFilter: enableFilter,
    disableFilter: disableFilter,
};

Fix bug where filter was not set to inacative


require('dotenv').config({
    silent: true,
})

let makeHostConfig = (env, prefix) => {
    let host = env[prefix + '_HOST'] || '127.0.0.1'
    let port = parseInt(env[prefix + '_PORT'], 10) || 3030
    let https = env[prefix + '_HTTPS'] === 'true' || env[prefix + '_HTTPS'] === true

    let default_port, protocol

    if (https) {
        default_port = 443
        protocol = 'https'
    } else {
        default_port = 80
        protocol = 'http'
    }

    let url = protocol + '://' + host + (port === default_port ? '' : ':' + port)

    return {
        host,
        port,
        url,
        protocol,
        https,
    }
}

const env = process.env.APP_ENV || 'local'

const atom = {
    announcement_url: process.env.ATOM_PLUGIN_ANNOUNCEMENT_URL || '#',
}

module.exports = {
    node: makeHostConfig(process.env, 'NODE'),
    api: makeHostConfig(process.env, 'API'),
    external: makeHostConfig(process.env, 'APP'),
    websocket: makeHostConfig(process.env, 'ECHO'),
    debug: process.env.APP_DEBUG === 'true' || process.env.APP_DEBUG === true,
    env,
    production: env === 'production',
    dev: env !== 'production',
    atom,
    analytics_track_id: process.env.GOOGLE_ANALYTICS_TRACK_ID
}

require('dotenv').config({
    silent: true,
})

let makeHostConfig = (env, prefix) => {
    let host = env[prefix + '_HOST'] || '127.0.0.1'
    let port = parseInt(env[prefix + '_PORT'], 10) || 3030
    let https = env[prefix + '_HTTPS'] === 'true' || env[prefix + '_HTTPS'] === true

    let default_port, protocol

    if (https) {
        default_port = 443
        protocol = 'https'
    } else {
        default_port = 80
        protocol = 'http'
    }

    let url = protocol + '://' + host + (port === default_port ? '' : ':' + port)

    return {
        host,
        port,
        url,
        protocol,
        https,
    }
}

const env = process.env.APP_ENV || 'local'

const atom = {
    announcement_url: process.env.ATOM_PLUGIN_ANNOUNCEMENT_URL || '#',
}

module.exports = {
    node: makeHostConfig(process.env, 'NODE'),
    api: makeHostConfig(process.env, 'API'),
    external: makeHostConfig(process.env, 'APP'),
    websocket: {
        host: process.env.ECHO_HOST || 'localhost',
        port: process.env.ECHO_PORT || '6002',
    },
    debug: process.env.APP_DEBUG === 'true' || process.env.APP_DEBUG === true,
    env,
    production: env === 'production',
    dev: env !== 'production',
    atom,
    analytics_track_id: process.env.GOOGLE_ANALYTICS_TRACK_ID
}

Implement HTTPS support for Docker


/**
 * @flow
 */

import React from 'react';
import { StyleSheet, TextInput } from 'react-native';
import PropTypes from 'prop-types';
import Box from './Box';

class EditableEntry extends React.Component {
  render() {
    return (
        <Box numberOfLines={1}>
            <TextInput
                style={styles.textInput}
                onChangeText={(text) => this.props.onChangeText(text)}
                onSubmitEditing={(event) => this.props.onSubmit(event.nativeEvent.text)}
                autoFocus={true}
                value={this.props.text}
            />
        </Box>
    );
  }
}

EditableEntry.propTypes = {
  text: PropTypes.string.isRequired,
  onChangeText: PropTypes.func.isRequired,
  onSubmit: PropTypes.func.isRequired
};

const styles = StyleSheet.create({
  textInput: {
    paddingTop: 6,
    paddingLeft: 6,
    height: 28,
    color: '#27ae60',
    alignItems: 'center',
    justifyContent: 'center',
    fontSize: 14,
    fontFamily: 'Helvetica',
    fontStyle: 'italic',
    fontWeight: 'bold',
  },
});

export default EditableEntry;

/**
 * @flow
 */

import React from 'react';
import { StyleSheet, TextInput } from 'react-native';
import PropTypes from 'prop-types';
import Box from './Box';

class EditableEntry extends React.Component {
  render() {
    return (
        <Box numberOfLines={1}>
            <TextInput
                style={styles.textInput}
                onChangeText={(text) => this.props.onChangeText(text)}
                onSubmitEditing={(event) => this.props.onSubmit(event.nativeEvent.text)}
                autoFocus={true}
                value={this.props.text}
                returnKeyType="done"
            />
        </Box>
    );
  }
}

EditableEntry.propTypes = {
  text: PropTypes.string.isRequired,
  onChangeText: PropTypes.func.isRequired,
  onSubmit: PropTypes.func.isRequired
};

const styles = StyleSheet.create({
  textInput: {
    paddingTop: 6,
    paddingLeft: 6,
    height: 28,
    color: '#27ae60',
    alignItems: 'center',
    justifyContent: 'center',
    fontSize: 14,
    fontFamily: 'Helvetica',
    fontStyle: 'italic',
    fontWeight: 'bold',
  },
});

export default EditableEntry;

Add a returnKeyType to the input keyboard


/**
 * @provides javelin-behavior-differential-user-select
 * @requires javelin-behavior
 *           javelin-dom
 *           javelin-stratcom
 */

JX.behavior('differential-user-select', function() {

  var unselectable;

  function isOnRight(node) {
    return node.parentNode.firstChild != node.previousSibling;
  }

  JX.Stratcom.listen(
    'mousedown',
    null,
    function(e) {
      var key = 'differential-unselectable';
      if (unselectable) {
        JX.DOM.alterClass(unselectable, key, false);
      }
      var diff = e.getNode('differential-diff');
      var td = e.getNode('tag:td');
      if (diff && td && isOnRight(td)) {
        unselectable = diff;
        JX.DOM.alterClass(diff, key, true);
      }
    });

});

/**
 * @provides javelin-behavior-differential-user-select
 * @requires javelin-behavior
 *           javelin-dom
 *           javelin-stratcom
 */

JX.behavior('differential-user-select', function() {

  var unselectable;

  function isOnRight(node) {
    return node.previousSibling &&
      node.parentNode.firstChild != node.previousSibling;
  }

  JX.Stratcom.listen(
    'mousedown',
    null,
    function(e) {
      var key = 'differential-unselectable';
      if (unselectable) {
        JX.DOM.alterClass(unselectable, key, false);
      }
      var diff = e.getNode('differential-diff');
      var td = e.getNode('tag:td');
      if (diff && td && isOnRight(td)) {
        unselectable = diff;
        JX.DOM.alterClass(diff, key, true);
      }
    });

});

Enable selecting text in Differential shield and gap


var Game = function(boardString){
  var board = '';
  this.board = '';
  if (arguments.length === 1) {
    this.board = this.toArray(boardString);
  } else {
    function random() {
      return Math.floor(Math.random() * 10 + 6);
    };
    var firstTwo = random(), secondTwo = random();
    for ( var i = 0; i < 16; i++ ) {
      if (i === firstTwo || i === secondTwo) {
        board += '2';
      } else {
        board += '0';
      };
    };
    this.board = this.toArray(board);
  }
};

Game.prototype = {

  toString: function() {
    for( var i = 0; i < 16; i += 4){
      this.array = this.board.slice(0 + i, 4 + i)
      console.log(this.array)
    }
  },

  toArray: function(chars) {
    var boardArray = [];
    for( var i = 0; i < 16; i += 4) {
      var subarray = chars.slice(0 + i, 4 + i);
      boardArray.push(subarray.split(''));
    }
    return boardArray;
  }
};

var Game = function(boardString){
  var board = '';
  this.board = '';
  if (arguments.length === 1) {
    this.board = this.toArray(boardString);
  } else {
    function random() {
      return Math.floor(Math.random() * 10 + 6);
    };
    var firstTwo = random(), secondTwo = random();
    for ( var i = 0; i < 16; i++ ) {
      if (i === firstTwo || i === secondTwo) {
        board += '2';
      } else {
        board += '0';
      };
    };
    this.board = this.toArray(board);
  }
};

Game.prototype = {

  toString: function() {
    this.board.forEach(function(row) {
      console.log(row.join(''));
    });
  },

  toArray: function(chars) {
    var boardArray = [];
    for( var i = 0; i < 16; i += 4) {
      var subarray = chars.slice(0 + i, 4 + i);
      boardArray.push(subarray.split(''));
    }
    return boardArray;
  }
};

Modify toString method for board array format


/**
 Configures a registry for use with an Ember-Data
 store.

 @method initializeStore
 @param {Ember.ApplicationInstance} applicationOrRegistry
 */
export default function initializeStoreService(applicationOrRegistry) {
  var registry, container;
  if (applicationOrRegistry.registry && applicationOrRegistry.container) {
    // initializeStoreService was registered with an
    // instanceInitializer. The first argument is the application
    // instance.
    registry = applicationOrRegistry.registry;
    container = applicationOrRegistry.container;
  } else {
    // initializeStoreService was called by an initializer instead of
    // an instanceInitializer. The first argument is a registy. This
    // case allows ED to support Ember pre 1.12
    registry = applicationOrRegistry;
    container = registry.container();
  }
  // Eagerly generate the store so defaultStore is populated.
  var store = container.lookup('store:application');
  registry.register('service:store', store, { instantiate: false });
}

/**
 Configures a registry for use with an Ember-Data
 store.

 @method initializeStore
 @param {Ember.ApplicationInstance} applicationOrRegistry
 */
export default function initializeStoreService(applicationOrRegistry) {
  var registry, container;
  if (applicationOrRegistry.registry && applicationOrRegistry.container) {
    // initializeStoreService was registered with an
    // instanceInitializer. The first argument is the application
    // instance.
    registry = applicationOrRegistry.registry;
    container = applicationOrRegistry.container;
  } else {
    // initializeStoreService was called by an initializer instead of
    // an instanceInitializer. The first argument is a registy. This
    // case allows ED to support Ember pre 1.12
    registry = applicationOrRegistry;
    if (registry.container) { // Support Ember 1.10 - 1.11
      container = registry.container();
    } else { // Support Ember 1.9
      container = registry;
    }
  }
  // Eagerly generate the store so defaultStore is populated.
  var store = container.lookup('store:application');
  registry.register('service:store', store, { instantiate: false });
}

Support for Ember 1.9's container API after `store:application` refactor.


var app = require('http').createServer(handler);
var io = require('socket.io').listen(app);
var fs = require('fs');
var util = require('util');
var Tail = require('tail').Tail;
var static = require('node-static');

var file = new static.Server('./web', {cache: 6}); //cache en secondes

function handler (req, res) {
    if( req.url == '/') {
        file.serveFile('/index.html', 200, {}, req, res);
    }

    req.addListener('end', function() {
        file.serve(req, res);
    }).resume();
}

app.listen(1337, '0.0.0.0');

function followFile(socket, file) {
    try {
        tail = new Tail(file);
    } catch (err) {
        socket.emit('cant follow', {reason: err.code, file: file});
        return;
    }

    tail.on("line", function(data) {
        socket.emit('line', {file:file, data:data});
    });

    socket.emit('followed', {file: file});
}

io.sockets.on('connection', function (socket) {
    socket.on('follow', function(data) {
        followFile(socket, data.file);
    });
});

var app = require('http').createServer(handler);
var io = require('socket.io').listen(app);
var fs = require('fs');
var util = require('util');
var Tail = require('tail').Tail;
var static = require('node-static');

var file = new static.Server('./web', {cache: 6}); //cache en secondes

var loadNbLines = 10;

function handler (req, res) {
    if( req.url == '/') {
        file.serveFile('/index.html', 200, {}, req, res);
    }

    req.addListener('end', function() {
        file.serve(req, res);
    }).resume();
}

app.listen(1337, '0.0.0.0');

function followFile(socket, file) {
    try {
        tail = new Tail(file);
    } catch (err) {
        socket.emit('cant follow', {reason: err.code, file: file});
        return;
    }
    fs.readFile(file, {encoding: "utf-8"}, function(err, data) {
        parts = data.split("\n");
        len = parts.length;
        if (len <= loadNbLines)
            start = 0;
        else
            start = len - loadNbLines - 1;
        for (i = start; i < len; i++) {
            socket.emit('line', {file:file, data:parts[i]});
        }
    });

    tail.on("line", function(data) {
        socket.emit('line', {file:file, data:data});
    });

    socket.emit('followed', {file: file});
}

io.sockets.on('connection', function (socket) {
    socket.on('follow', function(data) {
        followFile(socket, data.file);
    });
});

Read 10 lines from the file when starting to follow


import React, { PropTypes } from 'react';

import style from './style.css';
import Printer from '../printer';

class PrinterListComponent extends React.Component {

  getPrinterComponent(key) {
    return (<Printer
      {...this.props.printers[key]} key={key}
      toggleSelected={() => { this.props.toggleSelected(key); }} />);
  }

  generatePrinterList() {
    let keys = Object.keys(this.props.printers);
    let printerRows = [];
    if (keys.length > 0) {
      let howManyTimes = Math.floor(keys.length / 2);
      for (let i = 0; i < howManyTimes; i+=1) {
        console.log(Math.floor(keys.length/2));
        printerRows.push(

            {this.getPrinterComponent(keys[i * 2])}
            { keys[(i * 2) + 1] && this.getPrinterComponent(keys[(i * 2) + 1])}

);
      }
      return printerRows;
    }
    return (

);
  }

  render() {
    return (

        {this.generatePrinterList()}

);
  }
}

PrinterListComponent.propTypes = {
  printers: PropTypes.object.isRequired,
  toggleSelected: PropTypes.func.isRequired,
};

export default PrinterListComponent;

import React, { PropTypes } from 'react';

import style from './style.css';
import Printer from '../printer';

class PrinterListComponent extends React.Component {

  getPrinterComponent(key) {
    return (<Printer
      {...this.props.printers[key]} key={key}
      toggleSelected={() => { this.props.toggleSelected(key); }} />);
  }

  generatePrinterList() {
    let keys = Object.keys(this.props.printers);
    let printerRows = [];
    if (keys.length > 0) {
      let howManyTimes = Math.ceil(keys.length / 2);
      for (let i = 0; i < howManyTimes; i+=1) {
        printerRows.push(

            {this.getPrinterComponent(keys[i * 2])}
            { keys[(i * 2) + 1] && this.getPrinterComponent(keys[(i * 2) + 1])}

);
      }
      return printerRows;
    }
    return (

);
  }

  render() {
    return (

        {this.generatePrinterList()}

);
  }
}

PrinterListComponent.propTypes = {
  printers: PropTypes.object.isRequired,
  toggleSelected: PropTypes.func.isRequired,
};

export default PrinterListComponent;

Fix last printer not showing when there was odd number of printers


import Bot from 'telegram-api/build';
import Message from 'telegram-api/types/Message';
import subscribe from './commands/subscribe';
import doc from './commands/doc';

let bot = new Bot({
  token: '121143906:AAFJz_-Bjwq_8KQqTmyY2MtlcPb-bX_1O7M'
});

bot.start();

const welcome = new Message().text(`Hello!
I give you services to search across various sources such as MDN, GitHub, etc.

Commands:
/subscribe - Subscribe to /r/javascript and get a message for each new message
/unsubscribe - Unsubscribe
/doc [subject] - Search MDN for the given subject`);
bot.command('start', message => {
  bot.send(welcome.to(message.chat.id));
});

subscribe(bot);
doc(bot);

import Bot from 'telegram-api/build';
import Message from 'telegram-api/types/Message';
import subscribe from './commands/subscribe';
import doc from './commands/doc';

let bot = new Bot({
  token: '121143906:AAFJz_-Bjwq_8KQqTmyY2MtlcPb-bX_1O7M'
});

bot.start();

const COMMANDS = `Commands:
/subscribe - Subscribe to /r/javascript and get a message for each new message
/unsubscribe - Unsubscribe
/doc [subject] - Search MDN for the given subject
/github [subject] - Search GitHub for a repository
/npm [subject] - Search NPM for a package`;

const start = new Message().text(`Hello!
I give you services to search across various sources such as MDN, GitHub, etc.

${COMMANDS}`);

bot.command('start', message => {
  bot.send(start.to(message.chat.id));
});

const help = new Message().text(COMMANDS);
bot.command('help', message => {
  bot.send(help.to(message.chat.id));
});

subscribe(bot);
doc(bot);

Add help command Add github and npm to commands


'use strict';

(function() {

    describe("voteeHome", function() {

        beforeEach(function() {
            module('mean');
            module('mean.voteeHome');
        });

        var $controller;
        var $scope;

        beforeEach(inject(function(_$controller_){
            // The injector unwraps the underscores (_) from around the parameter names when matching
            $controller = _$controller_;
        }));

        describe("voteeHome controller", function() {
            beforeEach(function() {
                $scope = {};
                var controller = $controller('VoteeHomeController', { $scope: $scope });
            });

            it("stub", function() {
                expect(true).toBe(true);
            });
        });

        describe("header controller", function() {

            beforeEach(function() {
                $scope = {};
                var controller = $controller('VoteeHeaderController', { $scope: $scope });
            });

            it("Has a menu",
            function() {
                expect($scope.menus).toBeDefined();
                expect($scope.menus).not.toBeNull();
                expect($scope.menus.length).not.toBe(0);
            });
        });
    });

}());

'use strict';

(function() {

    describe("voteeHome", function() {

        beforeEach(function() {
            module('mean');
            module('mean.voteeHome');
        });

        var $controller;
        var $scope;

        beforeEach(inject(function(_$controller_){
            // The injector unwraps the underscores (_) from around the parameter names when matching
            $controller = _$controller_;
        }));

        describe("voteeHome controller", function() {
            beforeEach(function() {
                $scope = {};
                var controller = $controller('VoteeHomeController', { $scope: $scope });
            });

            it('should expose some global scope', function() {
              expect(true).toBeTruthy();
            });
        });

        describe("header controller", function() {

            beforeEach(function() {
                $scope = {};
                var controller = $controller('VoteeHeaderController', { $scope: $scope });
            });

            it("Has a menu",
            function() {
                expect($scope.menus).toBeDefined();
                expect($scope.menus).not.toBeNull();
                expect($scope.menus.length).not.toBe(0);
            });
        });
    });

}());

Use the 'should expose some global scope' test


/*
javascript in this file controls the html page demonstrating the autosubject functionality
*/

/**************************************************************************************/
/*              Set up and initialization */
/**************************************************************************************/
/*
initial setup - called from onLoad
attaches the autocomplete function to the search box
*/

var currentSuggestIndexDefault = "suggestall";  //initial default value

function setUpPage(number) {
	// connect the autoSubject to the input areas
	$('#keyword' + number).autocomplete( {
		source: autoSubjectExample,
		minLength: 1,
		select: function(event, ui) {
			$('#fastID' + number).val(ui.item.idroot);
			$('#fastType' + number).val(ui.item.tag);
			$('#fastInd' + number).val(ui.item.indicator);
		} //end select
	}
	).data( "autocomplete" )._renderItem = function( ul, item ) { formatSuggest(ul, item);};
}  //end setUpPage()

/*
	example style - simple reformatting
*/
function autoSubjectExample(request, response) {
	currentSuggestIndex = currentSuggestIndexDefault;
	autoSubject(request, response, exampleStyle);
}

/*
	For this example, replace the common subfield break of -- with  /
*/
function exampleStyle(res) {
	return res["auth"].replace("--","/") + ' (' + getTypeFromTag(res['tag']) + ')';
}

/*
javascript in this file controls the html page demonstrating the autosubject functionality
*/

/**************************************************************************************/
/*              Set up and initialization */
/**************************************************************************************/
/*
initial setup - called from onLoad
attaches the autocomplete function to the search box
*/

var currentSuggestIndexDefault = "suggest50";  //initial default value

function setUpPage(number) {
	// connect the autoSubject to the input areas
	$('#keyword' + number).autocomplete( {
		source: autoSubjectExample,
		minLength: 1,
		select: function(event, ui) {
			$('#fastID' + number).val(ui.item.idroot);
			$('#fastType' + number).val(ui.item.tag);
			$('#fastInd' + number).val(ui.item.indicator);
		} //end select
	}
	).data( "autocomplete" )._renderItem = function( ul, item ) { formatSuggest(ul, item);};
}  //end setUpPage()

/*
	example style - simple reformatting
*/
function autoSubjectExample(request, response) {
	currentSuggestIndex = currentSuggestIndexDefault;
	autoSubject(request, response, exampleStyle);
}

/*
	For this example, replace the common subfield break of -- with  /
*/
function exampleStyle(res) {
	return res["auth"].replace("--","/");
}

Revert "Display the tag/facet of the selected term in the input"


var mongoose     = require('mongoose');
var Schema       = mongoose.Schema;

var UserSchema   = new Schema(
{
    lastName : String,
    firstName : String,
    username : String,
    passwordHash : String,
    privateKey : String,
    picture : String,
    level : String,
    description : String,
    created : Date,
    birthday : Date,
    universityGroup : String,
    website : String,
});

module.exports = mongoose.model('User', UserSchema);

var mongoose     = require('mongoose');
var Schema       = mongoose.Schema;

var UserSchema   = new Schema(
{
    lastName : String,
    firstName : String,
    username : String,
    email : String,
    passwordHash : String,
    privateKey : String,
    picture : String,
    level : String,
    description : String,
    created : Date,
    birthday : Date,
    universityGroup : String,
    website : String,
});

module.exports = mongoose.model('User', UserSchema);

Add Email field to User


define(function () {
  // Hungarian
  return {
    errorLoading: function () {
      return 'Az eredmények betöltése nem sikerült.';
    },
    inputTooLong: function (args) {
      var overChars = args.input.length - args.maximum;

      return 'Túl hosszú. ' + overChars + ' karakterrel több, mint kellene.';
    },
    inputTooShort: function (args) {
      var remainingChars = args.minimum - args.input.length;

      return 'Túl rövid. Még ' + remainingChars + ' karakter hiányzik.';
    },
    loadingMore: function () {
      return 'Töltés…';
    },
    maximumSelected: function (args) {
      return 'Csak ' + args.maximum + ' elemet lehet kiválasztani.';
    },
    noResults: function () {
      return 'Nincs találat.';
    },
    searching: function () {
      return 'Keresés…';
    },
    removeAllItems: function () {
      return 'Távolítson el minden elemet';
    }
  };
});

define(function () {
  // Hungarian
  return {
    errorLoading: function () {
      return 'Az eredmények betöltése nem sikerült.';
    },
    inputTooLong: function (args) {
      var overChars = args.input.length - args.maximum;

      return 'Túl hosszú. ' + overChars + ' karakterrel több, mint kellene.';
    },
    inputTooShort: function (args) {
      var remainingChars = args.minimum - args.input.length;

      return 'Túl rövid. Még ' + remainingChars + ' karakter hiányzik.';
    },
    loadingMore: function () {
      return 'Töltés…';
    },
    maximumSelected: function (args) {
      return 'Csak ' + args.maximum + ' elemet lehet kiválasztani.';
    },
    noResults: function () {
      return 'Nincs találat.';
    },
    searching: function () {
      return 'Keresés…';
    },
    removeAllItems: function () {
      return 'Távolítson el minden elemet';
    },
    removeItem: function () {
      return 'Elem eltávolítása';
    },
    search: function() {
      return 'Keresés';
    }
  };
});

Update Hungarian translation for new strings


module.exports = {
  events: {
    udp: function (bot, msg, rinfo) {
      if (rinfo.address === bot.config.get('yakc.ip')) {
        bot.fireEvents('yakc', msg.toString(), rinfo)
      } else {
        bot.notice(bot.config.get('udp.channel'), msg.toString())
      }
    }
  }
}

module.exports = {
  events: {
    udp: function (bot, msg, rinfo) {
      if (rinfo.address === bot.config.get('yakc.ip')) {
        bot.fireEvents('yakc', msg.toString(), rinfo)
      } else {
        bot.notice(bot.config.get('udp.channel'), msg.toString())

        // copy https://github.com/zuzak/twitter-irc-udp messages from verified users:
        if (msg.toString().includes('✓')) {
          bot.notice(bot.config.get('irc.control'), msg.toString())
        }
      }
    }
  }
}

Copy interesting tweets to control channel


/* global fetch */

import React from 'react';
import TotalWidget from './TotalWidget';
import NextTripWidget from './NextTripWidget';
import TripTable from './TripTable';
import Map from './Map';
import './scss/overview.scss';

class Overview extends React.Component {
  constructor() {
    super();

    this.loading = true;
    this.state = {
      trips: []
    };
  }

  componentDidMount() {
    this.getData();
  }

  getData() {
    fetch('http://localhost:4444/trips')
      .then((response) => {
        return response.json();
      })
      .then((trips) => {
        this.loading = false;
        this.setState({ trips });
      });
  }

  render() {
    return (

        { this.loading ? Loading.. :

            <NextTripWidget
              color="light"
              nextTrip={this.state.trips.next}
            />
            <TotalWidget
              color="medium"
              totals={this.state.trips.total}
            />

          <Map
            countries={this.state.trips.allCountries}
            selector="visitedMap"
          />
          <TripTable
            trips={this.state.trips.visited}
          />

Wishlist

          <Map
            countries={this.state.trips.wishlist}
            selector="wishlistMap"
          />

        }

    );
  }
}

export default Overview;

/* global fetch document */

import React from 'react';
import TotalWidget from './TotalWidget';
import NextTripWidget from './NextTripWidget';
import TripTable from './TripTable';
import Map from './Map';
import './scss/overview.scss';

class Overview extends React.Component {
  constructor() {
    super();

    this.loading = true;
    this.state = {
      trips: []
    };
  }

  componentDidMount() {
    this.getData();
  }

  getData() {
    fetch(`${document.location.protocol}//${document.location.hostname}:4444/trips`)
      .then((response) => {
        return response.json();
      })
      .then((trips) => {
        this.loading = false;
        this.setState({ trips });
      });
  }

  render() {
    return (

        { this.loading ? Loading.. :

            <NextTripWidget
              color="light"
              nextTrip={this.state.trips.next}
            />
            <TotalWidget
              color="medium"
              totals={this.state.trips.total}
            />

          <Map
            countries={this.state.trips.allCountries}
            selector="visitedMap"
          />
          <TripTable
            trips={this.state.trips.visited}
          />

Wishlist

          <Map
            countries={this.state.trips.wishlist}
            selector="wishlistMap"
          />

        }

    );
  }
}

export default Overview;

Make fetch URL less specific


Accounts.ui.config({
  requestPermissions: {
    github: ['user', 'repo', 'gist']
  }
})
//
// Accounts.onLogin(function () {
//   Router.go('/profile')
// })

Template.login.helpers({
  currentUser: function () {
    return Meteor.user()
  }
})

Template.login.events({
})

Accounts.ui.config({
  requestPermissions: {
    github: ['user', 'public_gist']
  }
})
//
// Accounts.onLogin(function () {
//   Router.go('/profile')
// })

Template.login.helpers({
  currentUser: function () {
    return Meteor.user()
  }
})

Template.login.events({
})

Update access request for public gists


$(document).ready(function(){

  // Include common elements
  $("#footer").load("common/footer.html");
  $("#header").load("common/header.html", function()
  {
    // Add current navigation class based on url
    locations = location.pathname.split("/");
    $('#topnav a[href^="' + locations[locations.length - 1] + '"]').parent().addClass('current');

    /**
    * Handles toggling the navigation menu for small screens.
    */

    var button = document.getElementById( 'topnav' ).getElementsByTagName( 'div' )[0],
        menu   = document.getElementById( 'topnav' ).getElementsByTagName( 'ul' )[0];

    if ( undefined === button )
      return false;

    if ( undefined === menu || ! menu.childNodes.length )
    {
      button.style.display = 'none';
      return false;
    }

    button.onclick = function()
    {
      if ( -1 == menu.className.indexOf( 'srt-menu' ) )
        menu.className = 'srt-menu';

      if ( -1 != button.className.indexOf( 'toggled-on' ) ) {
        button.className = button.className.replace( ' toggled-on', '' );
        menu.className = menu.className.replace( ' toggled-on', '' );
      } else {
        button.className += ' toggled-on';
        menu.className += ' toggled-on';
      }
    };
  });
});

$(document).ready(function(){

  // Include common elements
  $("#footer").load("common/footer.html");
  $("#header").load("common/header.html", function()
  {
    // Add current navigation class based on url
    locations = location.pathname.split("/");
    current = $('#topnav a[href^="' + locations[locations.length - 1] + '"]')
    if (current.length == 0)
    {
      current = $('#topnav a[href^="index.html"]');
    }
    current.parent().addClass('current');

    /**
    * Handles toggling the navigation menu for small screens.
    */

    var button = document.getElementById( 'topnav' ).getElementsByTagName( 'div' )[0],
        menu   = document.getElementById( 'topnav' ).getElementsByTagName( 'ul' )[0];

    if ( undefined === button )
      return false;

    if ( undefined === menu || ! menu.childNodes.length )
    {
      button.style.display = 'none';
      return false;
    }

    button.onclick = function()
    {
      if ( -1 == menu.className.indexOf( 'srt-menu' ) )
        menu.className = 'srt-menu';

      if ( -1 != button.className.indexOf( 'toggled-on' ) ) {
        button.className = button.className.replace( ' toggled-on', '' );
        menu.className = menu.className.replace( ' toggled-on', '' );
      } else {
        button.className += ' toggled-on';
        menu.className += ' toggled-on';
      }
    };
  });
});

Select first nav li by default


var updatePreviewTarget = function(data, status) {
  $('.spinner').hide();
  $('#preview-target').html(data);
  renderLatexExpressions();
}

var renderLatexExpressions = function() {
  $('.latex-container').each(
    function(index) {
      katex.render(
        this.textContent,
        this,
        { displayMode: $(this).data('displaymode') == 'block' }
      );
    }
  );
}

var main = function() {
  $('div code').each(function(i, block) {
    hljs.highlightBlock(block);
  });

  $('button#preview').click(
    function() {
      $('#preview-target').html('');
      $('.spinner').show();
      $('h1.preview').text($('input[name=title]').val())
      $.post('/note_preview', {text: $('#note-txt').val()}, updatePreviewTarget);
    }
  );

  renderLatexExpressions();
};

function searchRedirect(event, searchpage) {

  var term = (arguments.length == 2) ? document.getElementById('mainsearch').value : document.getElementById('searchbox').value;

  window.location = '/search/' + term + '/';

  event.preventDefault(event);

}

$(main);

var updatePreviewTarget = function(data, status) {
  $('.spinner').hide();
  $('#preview-target').html(data);
  renderLatexExpressions();
}

var renderLatexExpressions = function() {
  $('.latex-container').each(
    function(index) {
      katex.render(
        this.textContent,
        this,
        { displayMode: $(this).data('displaymode') == 'block' }
      );
    }
  );
}

var main = function() {
  $('div code').each(function(i, block) {
    hljs.highlightBlock(block);
  });

  $('button#preview').click(
    function() {
      $('#preview-target').html('');
      $('.spinner').show();
      $('h1.preview').text($('input[name=title]').val())
      $.post('/note_preview', {text: $('#note-txt').val()}, updatePreviewTarget);
    }
  );

  renderLatexExpressions();

  $("textarea[name='footnotes']").focusin(function(){
    $(this).height($(this).height() + 300);
  }).focusout(function(){
    $(this).height($(this).height() - 300);
  });
};

function searchRedirect(event, searchpage) {

  var term = (arguments.length == 2) ? document.getElementById('mainsearch').value : document.getElementById('searchbox').value;

  window.location = '/search/' + term + '/';

  event.preventDefault(event);

}

$(main);

Make footnote textarea expand upon focus


var casper = require('casper').create();

// casper.on('remote.message', function(message) {
//     console.log(message);
// });

var out_dir = 'static/tmp/';
var key = casper.cli.get(0);
var url = casper.cli.get(1);

var filepath = out_dir + key + '.png';

casper.start();

casper.viewport(1024, 650).thenOpen(url, function() {
    this.wait(1000);
    this.capture(filepath);
    casper.echo(filepath);
});

casper.run();

var casper = require('casper').create();

// casper.on('remote.message', function(message) {
//     console.log(message);
// });

var out_dir = 'static/tmp/';
var key = casper.cli.get(0);
var url = casper.cli.get(1);

var filepath = out_dir + key + '.png';

casper.start();

casper.viewport(1024, 650).thenOpen(url, function() {
    casper.then(function() {
        this.wait(2000, (function() {
            return function() {
                casper.capture(filepath);
                casper.echo(filepath);
            }
        })())
    });
});

casper.run();

Use casper.wait to wait until image are loaded and transitioned


'use strict';
var app = require('app');
var dribbbleapi = require('./dribbbleapi');

var mainWindow = null;
var dribbbleData = null;

var menubar = require('menubar')

var mb = menubar({
  index: 'file://' + __dirname + '/app/index.html',
  icon: __dirname + '/app/assets/HotshotIcon.png',
  width: 400,
  height: 700,
  'max-width': 440,
  'min-height': 300,
  'min-width': 300,
  preloadWindow: true
});

app.on('window-all-closed', function() {
  app.quit();
});

mb.on('show', function(){
//  mb.window.webContents.send('focus');
  mb.window.openDevTools();
});

'use strict';
var app = require('app');

var mainWindow = null;
var dribbbleData = null;

var menubar = require('menubar')

var mb = menubar({
  index: 'file://' + __dirname + '/app/index.html',
  icon: __dirname + '/app/assets/HotshotIcon.png',
  width: 400,
  height: 700,
  'max-width': 440,
  'min-height': 300,
  'min-width': 300,
  preloadWindow: true
});

app.on('window-all-closed', function() {
  app.quit();
});

mb.on('show', function(){
//  mb.window.webContents.send('focus');
  mb.window.openDevTools();
});

Remove reference to deleted dribbbleApi.js


/* eslint-env node */
'use strict';

module.exports = {
  allowedVersions: {
    'ember-in-element-polyfill': '*',
    'ember-get-config': '0.3.0 || 0.4.0 || 0.5.0',
    '@embroider/util': '*',
  },
};

/* eslint-env node */
'use strict';

module.exports = {
  allowedVersions: {
    'ember-in-element-polyfill': '*',
  },
};

Remove unneeded dependency lint overrides


// ==UserScript==
// @name        Rightmove Enhancement Suite
// @namespace   https://github.com/chigley/
// @description Keyboard shortcuts
// @include     http://www.rightmove.co.uk/*
// @version     1
// @grant       GM_addStyle
// @grant       GM_getResourceText
// @resource    style style.css
// ==/UserScript==

var $ = unsafeWindow.jQuery;

GM_addStyle(GM_getResourceText("style"));

$("#summaries li").mouseenter(function () {
	$(this).addClass("hovered");
});

$("#summaries li").mouseleave(function () {
	$(this).removeClass("hovered");
});

// ==UserScript==
// @name        Rightmove Enhancement Suite
// @namespace   https://github.com/chigley/
// @description Keyboard shortcuts
// @include     http://www.rightmove.co.uk/*
// @version     1
// @grant       GM_addStyle
// @grant       GM_getResourceText
// @resource    style style.css
// ==/UserScript==

var $ = unsafeWindow.jQuery;

GM_addStyle(GM_getResourceText("style"));

$("[name=summary-list-item]").mouseenter(function () {
	$(this).addClass("hovered");
});

$("[name=summary-list-item]").mouseleave(function () {
	$(this).removeClass("hovered");
});

Fix selector to only match list items


// Ember!
import Ember from 'ember';
import SearchRoute from 'yebo-ember-storefront/mixins/search-route';

/**
 * Taxons show page
 * This page shows products related to the current taxon
 */
export default Ember.Route.extend(SearchRoute, {
  /**
   * Define the search rules
   */
  searchRules(query, params) {
    // Create a new taxonomy rule
    let rule = new YeboSDK.Products.Rules.taxonomy([]);

    // Set its values
    rule.values = [params.taxon];

    // Set it into the query
    query.and(rule);
  },

  // Change the current controller
  setupController: function(controller, model) {
    // This is indispensable, if out, the model won't be passed to the view
    controller.set('model', model);

    // the component that requires current taxon and taxonomies is in application
    let appController = this.controllerFor('application');

    // Define some values
    let taxon = model.taxon;
    let taxonomies = model.taxonomies;

    // Set the values to the application controller
    appController.set('currentTaxonomy', taxon.get('taxonomy'));
    appController.set('taxonomies', taxonomies);
  },

  /**
   * This values will be returned into the route (with the route model)
   */
  searchModel(params) {
    // Search all the taxonomies and the current taxon
    return {
      taxonomies: this.yebo.store.findAll('taxonomy'),
      taxon: this.yebo.store.find('taxon', params.taxon),
    };
  }
});

// Ember!
import Ember from 'ember';
import SearchRoute from 'yebo-ember-storefront/mixins/search-route';

/**
 * Taxons show page
 * This page shows products related to the current taxon
 */
export default Ember.Route.extend(SearchRoute, {
  /**
   * Define the search rules
   */
  searchRules(query, params) {
    // Create a new taxonomy rule
    let rule = new YeboSDK.Products.Rules.taxonomy([]);

    // Set its values
    rule.values = [params.taxon];

    // Set it into the query
    query.and(rule);
  },

  // Change the current controller
  setupController: function(controller, model) {
    // This is indispensable, if out, the model won't be passed to the view
    controller.set('model', model);

    // the component that requires current taxon and taxonomies is in application
    let appController = this.controllerFor('application');

    // Define some values
    let taxon = model.taxon;
    let taxonomies = model.taxonomies;

    // Set the values to the application controller
    appController.set('currentTaxonomy', taxon.get('taxonomy'));
    appController.set('taxonomies', taxonomies);
  },

  /**
   * This values will be returned into the route (with the route model)
   */
  searchModel(params) {
    // Search all the taxonomies and the current taxon
    return Ember.RSVP.hash({
      taxonomies: this.yebo.store.findAll('taxonomy'),
      taxon: this.yebo.store.find('taxon', params.taxon),
    });
  }
});

Return a promise in the searchModel method


// ***********************************************
// This example commands.js shows you how to
// create various custom commands and overwrite
// existing commands.
//
// For more comprehensive examples of custom
// commands please read more here:
// https://on.cypress.io/custom-commands
// ***********************************************
//
//
// -- This is a parent command --
// Cypress.Commands.add("login", (email, password) => { ... })
//
//mm
// -- This is a child command --
// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
//
//
// -- This is a dual command --
// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
//
//
// -- This will overwrite an existing command --
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })

Cypress.Commands.add('setRadioInput', (selector, value) => {
  const selectorValue = value === 'Yes' ? '0' : '1'
  cy.get(`#${selector}-${selectorValue}`).check();
})

Cypress.Commands.add('setTextInput', (selector, value) => {
  cy.get(`#${selector}`).type(value);
})

Cypress.Commands.add('setCheckboxInput', (selector) => {
  cy.get(`#${selector}`).check();
})

Add functionality for filling in checkboxes


import React from "react";
import {
  Text
} from "react-native";

export default class MemoriesScreen extends React.Component {
  render() {
    return (
      <Text>My memories</Text>
    );
  }
}

MemoriesScreen.route = {
  navigationBar: {
    title: "My memories",
  }
};

import React from "react";
import {
  Text
} from "react-native";

var memory = {
  eventName: "Tree planting",
  eventDate: Date(),
  image: require("../static/content/images/memories/01.jpg")
};

var userData = {
  name: "Sara",
  image: require("../static/content/images/people/users/01_sara_douglas.jpg"),
  memories: []
};

for (var i = 0; i < 10; i++) {
  userData.memories.push(memory);
}

export default class MemoriesScreen extends React.Component {
  render() {
    return (
      <Text>My memories</Text>
    );
  }
}

MemoriesScreen.route = {
  navigationBar: {
    title: "My memories",
  }
};

Add sample data example to memories screen for UI dev


var endGame = function(game){

  if (game.player.lives > 0) {
    missBall(game.ball, game.player)
  } else if (game.player.lives <= 0 ){
    gameOver()
  }
}

var missBall = function(ball, player){
  if ( ball.bottomEdge().y == canvas.height){
    return player.loseLife()
  }
  return false
}

function gameOver() {
  console.log('game over!')
  alert('Game Over!');
  window.location.reload()
}

var endGame = function(game){

  if (game.player.lives > 0) {
    missBall(game.ball, game.player)
  } else if (game.player.lives <= 0 ){
    gameOver()
  }
  if (stillBricks(game.bricks)) {
    winGame()
  }
}

var missBall = function(ball, player){
  if ( ball.bottomEdge().y == canvas.height){
    console.log("Lost Life")
    return player.loseLife()
  };
}

function gameOver() {
  console.log('game over!')
  alert('Game Over!');
  window.location.reload()
}

function winGame() {
  console.log("You Win!")
  alert("You are the Winner")
  window.location.reload()
}

var stillBricks = function (bricks) {
  var counter = 0
  for (var layer in bricks){
    bricks[layer].map(function(brick){
      if (brick == null){
        counter += 1;
      };
    });
  }
  if (counter == 16){
    return true
  }
}

Implement win game prompt after all bricks destroyed


document.addEventListener('DOMContentLoaded', function() {
  var searchButton = document.getElementById('search');

  searchButton.addEventListener('click', (function() {
    var query = document.getElementById('form').query.value;
    var engine = document.querySelector('input[name="engine"]:checked').value;
    var urls = {
      'hackage': 'https://www.haskell.org/hoogle/?hoogle=',
      'lts': 'https://www.stackage.org/lts/hoogle?q=',
      'nightly': 'https://www.stackage.org/nightly/hoogle?q='
    };
    var url = urls[engine] + query;
    chrome.tabs.create({url: url});
  }));
});

document.addEventListener('DOMContentLoaded', function() {
  var searchButton = document.getElementById('search');
  document.querySelector('input[name="engine"]:checked').focus();

  searchButton.addEventListener('click', (function() {
    var query = document.getElementById('form').query.value;
    var engine = document.querySelector('input[name="engine"]:checked').value;
    var urls = {
      'hackage': 'https://www.haskell.org/hoogle/?hoogle=',
      'lts': 'https://www.stackage.org/lts/hoogle?q=',
      'nightly': 'https://www.stackage.org/nightly/hoogle?q='
    };
    var url = urls[engine] + query;
    chrome.tabs.create({url: url});
  }));
});

Add focus on the first radio button


(function(){

'use strict';

angular
  .module('secondLead')

  .controller('RegisterCtrl', [
    'Restangular',
    '$state',
    'store',
    'UserModel',
    function (Restangular, $state, store, UserModel) {
    var register = this;

    register.newUser = {};

    register.onSubmit = function () {
      UserModel.register(register.newUser)
      	.then(function(response){
          if (response.errors){
            register.error = response.errors[0];
          } else {
            store.set('jwt',response.token);
            store.set('user',response.user);
            $state.go('user.lists', {userID: response.user.id});
            register.reset();
          }
        });
    };

    register.reset = function () {
      register.newUser = {};
    };

  }])

})();

(function(){

'use strict';

angular
  .module('secondLead')

  .controller('RegisterCtrl', [
    'Restangular',
    '$state',
    'store',
    'UserModel',
    function (Restangular, $state, store, UserModel) {
    var register = this;

    register.newUser = {};

    register.onSubmit = function () {
      UserModel.register(register.newUser)
      	.then(function(response){
          if (response.errors){
            register.error = response.errors[0];
          } else {
            UserModel.setLoggedIn(true);
            store.set('jwt',response.token);
            store.set('user',response.user);
            $state.go('user.lists', {userID: response.user.id});
            register.reset();
          }
        });
    };

    register.reset = function () {
      register.newUser = {};
    };

  }])

})();

Add update to usermodel upon signup with register controller


var cadence = require('cadence')
var util = require('util')

function UserAgent (ua) {
    this._ua = ua
}

UserAgent.prototype.send = cadence(function (async, properties, body) {
    var url = util.format('http://%s/kibitz', properties.location)
    this._ua.fetch({
        url: url,
        post: {
            islandName: properties.islandName,
            colleagueId: properties.colleagueId,
            kibitz: body
        },
        nullify: true
    }, async())
})

module.exports = UserAgent

var cadence = require('cadence')
var util = require('util')

function UserAgent (ua) {
    this._ua = ua
}

UserAgent.prototype.send = cadence(function (async, properties, body) {
    var url = util.format('http://%s/kibitz', properties.location)
    this._ua.fetch({
        url: url,
        post: {
            key: '(' + properties.islandName + ')' + properties.colleagueId,
            post: { kibitz: body }
        },
        nullify: true
    }, async())
})

module.exports = UserAgent

Adjust user agent for new keyed protocol.


/**
 * Internal dependencies
 */
import { addAMPExtraProps } from '../';

describe( 'addAMPExtraProps', () => {
	it( 'does not modify non-child blocks', () => {
		const props = addAMPExtraProps( {}, { name: 'foo/bar' }, {} );

		expect( props ).toStrictEqual( {} );
	} );

	it( 'generates a unique ID', () => {
		const props = addAMPExtraProps( {}, { name: 'amp/amp-story-text' }, {} );

		expect( props ).toHaveProperty( 'id' );
	} );

	it( 'uses the existing anchor attribute as the ID', () => {
		const props = addAMPExtraProps( {}, { name: 'amp/amp-story-text' }, { anchor: 'foo' } );

		expect( props ).toStrictEqual( { id: 'foo' } );
	} );

	it( 'adds a font family attribute', () => {
		const props = addAMPExtraProps( {}, { name: 'amp/amp-story-text' }, { ampFontFamily: 'Roboto' } );

		expect( props ).toMatchObject( { 'data-font-family': 'Roboto' } );
	} );

	it( 'adds inline CSS for rotation', () => {
		const props = addAMPExtraProps( {}, { name: 'amp/amp-story-text' }, { rotationAngle: 90.54321 } );

		expect( props ).toMatchObject( { style: { transform: 'rotate(90deg)' } } );
	} );
} );

/**
 * Internal dependencies
 */
import { addAMPExtraProps } from '../';

describe( 'addAMPExtraProps', () => {
	it( 'does not modify non-child blocks', () => {
		const props = addAMPExtraProps( {}, { name: 'foo/bar' }, {} );

		expect( props ).toStrictEqual( {} );
	} );

	it( 'adds a font family attribute', () => {
		const props = addAMPExtraProps( {}, { name: 'amp/amp-story-text' }, { ampFontFamily: 'Roboto' } );

		expect( props ).toMatchObject( { 'data-font-family': 'Roboto' } );
	} );

	it( 'adds inline CSS for rotation', () => {
		const props = addAMPExtraProps( {}, { name: 'amp/amp-story-text' }, { rotationAngle: 90.54321 } );

		expect( props ).toMatchObject( { style: { transform: 'rotate(90deg)' } } );
	} );
} );

Remove tests that are not relevant anymore.


(function() {

  var global = this;
  var karma = global.__karma__;

  karma.start = function(runner) {
    var suites = global.__karma_benchmark_suites__;
    var hasTests = !!suites.length;
    var errors = [];

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

    runNextSuite();

    function logResult(event) {
      var suite = this;
      var result = event.target;
      karma.result({
        id: result.id,
        description: suite.name+': '+result.name,
        suite: [],
        success: errors.length === 0,
        log: errors,
        skipped: false,
        time: result.stats.mean * 1000,
        benchmark: {
          suite: suite.name,
          name: result.name,
          stats: result.stats,
          count: result.count,
          cycles: result.cycles,
          error: result.error,
          hz: result.hz
        }
      });
    }

    function logError(evt) {
      errors.push(evt.target.error);
    }

    function runNextSuite() {
      if (!suites.length) {
        return complete();
      }

      suites.shift()
      .on('cycle', logResult)
      .on('abort error', logError)
      .on('complete', runNextSuite)
      .run({
        async: true
      });
    }

    function complete() {
      karma.complete({
        coverage: global.__coverage__
      });
    }
  };

}).call(this);

(function() {

  var global = this;
  var karma = global.__karma__;

  karma.start = function(runner) {
    var suites = global.__karma_benchmark_suites__;
    var hasTests = !!suites.length;
    var errors = [];

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

    runNextSuite();

    function logResult(event) {
      var suite = this;
      var result = event.target;
      karma.result({
        id: result.id,
        description: suite.name+': '+result.name,
        suite: [],
        success: errors.length === 0,
        log: errors,
        skipped: false,
        time: result.stats.mean * 1000,
        benchmark: {
          suite: suite.name,
          name: result.name,
          stats: result.stats,
          count: result.count,
          cycles: result.cycles,
          error: result.error,
          hz: result.hz
        }
      });

      // Reset errors
      errors = [];
    }

    function logError(evt) {
      errors.push(evt.target.error.toString());
    }

    function runNextSuite() {
      if (!suites.length) {
        return complete();
      }

      suites.shift()
      .on('cycle', logResult)
      .on('abort error', logError)
      .on('complete', runNextSuite)
      .run({
        async: true
      });
    }

    function complete() {
      karma.complete({
        coverage: global.__coverage__
      });
    }
  };

}).call(this);

Fix logging of errors, Karma expects a string. Reset errors after every cycle.


import middleware from './internal/middleware'
export default middleware

export { runSaga } from './internal/runSaga'
export { END, eventChannel, channel } from './internal/channel'
export { buffers } from './internal/buffers'
export { takeEvery, takeLatest } from './internal/sagaHelpers'

import { CANCEL } from './internal/proc'
import * as effects from './effects'
import * as utils from './utils'

export function delay(ms, val=true) {
  let timeoutId;
  const promise = new Promise((resolve) => {
    timeoutId = setTimeout(resolve, ms, val);
  });

  promise[CANCEL] = () => clearTimeout(timeoutId);

  return promise;
}

export {
  CANCEL,
  effects,
  utils
}

import middleware from './internal/middleware'
export default middleware

export { runSaga } from './internal/runSaga'
export { END, eventChannel, channel } from './internal/channel'
export { buffers } from './internal/buffers'
export { takeEvery, takeLatest } from './internal/sagaHelpers'

import { CANCEL } from './internal/proc'
import * as effects from './effects'
import * as utils from './utils'

export function delay(ms, val=true) {
  let timeoutId;
  const promise = new Promise((resolve) => {
    timeoutId = setTimeout(() => resolve(val), ms);
  });

  promise[CANCEL] = () => clearTimeout(timeoutId);

  return promise;
}

export {
  CANCEL,
  effects,
  utils
}

Replace `setTimeout` call with 2-args version


(function(app) {
  'use strict';

  var dom = app.dom || require('../dom.js');

  var GeoNamesData = function(url) {
    this._url = url;
    this._loadPromise = null;
    this._data = [];
  };

  GeoNamesData.prototype.load = function() {
    if (!this._loadPromise) {
      this._loadPromise = dom.loadJSON(this._url).then(function(data) {
        this._data = data;
      }.bind(this));
    }
    return this._loadPromise;
  };

  if (typeof module !== 'undefined' && module.exports) {
    module.exports = GeoNamesData;
  } else {
    app.GeoNamesData = GeoNamesData;
  }
})(this.app || (this.app = {}));

(function(app) {
  'use strict';

  var dom = app.dom || require('../dom.js');
  var Events = app.Events || require('../base/events.js');

  var GeoNamesData = function(url) {
    this._url = url;
    this._loadPromise = null;
    this._data = [];
    this._events = new Events();
  };

  GeoNamesData.prototype.on = function() {
    return Events.prototype.on.apply(this._events, arguments);
  };

  GeoNamesData.prototype.load = function() {
    if (!this._loadPromise) {
      this._events.emit('loading');
      this._loadPromise = dom.loadJSON(this._url).then(function(data) {
        this._data = data;
        this._events.emit('loaded');
      }.bind(this));
    }
    return this._loadPromise;
  };

  if (typeof module !== 'undefined' && module.exports) {
    module.exports = GeoNamesData;
  } else {
    app.GeoNamesData = GeoNamesData;
  }
})(this.app || (this.app = {}));

Make events of GeoNames data


import React from 'react';
import { BaseViewComponent } from '../higher-order/index.js';

class RedditPosts extends BaseViewComponent {
  constructor(props) {
    super(props);
  }

  render() {
    return (

Fetch posts

          {this.props.posts.map(function renderPosts(post) {
            return (

{post.title}

            );
          })}

    );
  }
}

RedditPosts.propTypes = {
  posts: React.PropTypes.array.isRequired,
  fetchPosts: React.PropTypes.func.isRequired
};

export default RedditPosts;

import React from 'react';
import { Avatar, List, ListItem } from 'material-ui';
import { BaseViewComponent } from '../higher-order/index.js';

class RedditPosts extends BaseViewComponent {
  constructor(props) {
    super(props);
  }

  render() {
    return (

Fetch posts

          <List>
          {this.props.posts.map(function renderPosts(post) {
            let avatar = (<Avatar src={post.thumbnail} />);
            return (
              <ListItem leftAvatar={avatar}>

                  {post.title}

              </ListItem>
            );
          })}

          </List>

    );
  }
}

RedditPosts.propTypes = {
  posts: React.PropTypes.array.isRequired,
  fetchPosts: React.PropTypes.func.isRequired
};

export default RedditPosts;

Make songs-list a list of list-items


// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

/**
 * This view displays information on the HTTP cache.
 *  @constructor
 */
function HttpCacheView() {
  const mainBoxId = 'http-cache-view-tab-content';
  const statsDivId = 'http-cache-view-cache-stats';

  DivView.call(this, mainBoxId);

  this.statsDiv_ = $(statsDivId);

  // Register to receive http cache info.
  g_browser.addHttpCacheInfoObserver(this);
}

inherits(HttpCacheView, DivView);

HttpCacheView.prototype.onLoadLogFinish = function(data) {
  return this.onHttpCacheInfoChanged(data.httpCacheInfo);
};

HttpCacheView.prototype.onHttpCacheInfoChanged = function(info) {
  this.statsDiv_.innerHTML = '';

  if (!info)
    return false;

  // Print the statistics.
  var statsUl = addNode(this.statsDiv_, 'ul');
  for (var statName in info.stats) {
    var li = addNode(statsUl, 'li');
    addTextNode(li, statName + ': ' + info.stats[statName]);
  }
  return true;
};

// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

/**
 * This view displays information on the HTTP cache.
 */

var HttpCacheView = (function() {
  // IDs for special HTML elements in http_cache_view.html
  const MAIN_BOX_ID = 'http-cache-view-tab-content';
  const STATS_DIV_ID = 'http-cache-view-cache-stats';

  // We inherit from DivView.
  var superClass = DivView;

  /**
   *  @constructor
   */
  function HttpCacheView() {
    // Call superclass's constructor.
    superClass.call(this, MAIN_BOX_ID);

    this.statsDiv_ = $(STATS_DIV_ID);

    // Register to receive http cache info.
    g_browser.addHttpCacheInfoObserver(this);
  }

  cr.addSingletonGetter(HttpCacheView);

  HttpCacheView.prototype = {
    // Inherit the superclass's methods.
    __proto__: superClass.prototype,

    onLoadLogFinish: function(data) {
      return this.onHttpCacheInfoChanged(data.httpCacheInfo);
    },

    onHttpCacheInfoChanged: function(info) {
      this.statsDiv_.innerHTML = '';

      if (!info)
        return false;

      // Print the statistics.
      var statsUl = addNode(this.statsDiv_, 'ul');
      for (var statName in info.stats) {
        var li = addNode(statsUl, 'li');
        addTextNode(li, statName + ': ' + info.stats[statName]);
      }
      return true;
    }
  };

  return HttpCacheView;
})();

Refactor HttpCacheView to be defined inside an anonymous namespace.


import React from 'react';
import PropTypes from 'prop-types';
import { Link } from 'react-router-dom';
import { msgid, c } from 'ttag';
import { SimpleDropdown, DropdownMenu } from 'react-components';

const MemberAddresses = ({ member, addresses }) => {
    const list = addresses.map(({ ID, Email }) => (

            {Email}

    ));
    const n = list.length;
    const addressesTxt = ` ${c('Info').ngettext(msgid`address`, `addresses`, n)}`;
    const contentDropDown = (
        <>
            {n}
            {addressesTxt}
        </>
    ); // trick for responsive and mobile display

    return (
        <>
            <SimpleDropdown className="pm-button--link" content={contentDropDown}>
                <DropdownMenu>{list}</DropdownMenu>

                    <Link className="pm-button" to={`/settings/addresses/${member.ID}`}>{c('Link').t`Manage`}</Link>

            </SimpleDropdown>
        </>
    );
};

MemberAddresses.propTypes = {
    member: PropTypes.object.isRequired,
    addresses: PropTypes.array.isRequired,
};

export default MemberAddresses;

import React from 'react';
import PropTypes from 'prop-types';
import { Link } from 'react-router-dom';
import { msgid, c } from 'ttag';
import { SimpleDropdown, DropdownMenu } from 'react-components';

const MemberAddresses = ({ member, addresses }) => {
    const list = addresses.map(({ ID, Email }) => (

            {Email}

    ));
    const n = list.length;
    const addressesTxt = ` ${c('Info').ngettext(msgid`address`, `addresses`, n)}`;
    const contentDropDown = (
        <>
            {n}
            {addressesTxt}
        </>
    ); // trick for responsive and mobile display

    return (
        <>
            <SimpleDropdown className="pm-button--link" content={contentDropDown}>

                    <Link className="pm-button w100 aligncenter" to={`/settings/addresses/${member.ID}`}>{c('Link')
                        .t`Manage`}</Link>

                <DropdownMenu>{list}</DropdownMenu>
            </SimpleDropdown>
        </>
    );
};

MemberAddresses.propTypes = {
    member: PropTypes.object.isRequired,
    addresses: PropTypes.array.isRequired,
};

export default MemberAddresses;

Fix dropdown width in adresses settings


/**
 * Tslint webpack block.
 *
 * @see https://github.com/wbuchwalter/tslint-loader
 */

module.exports = tslint

/**
 * @param {object}   [options]                  See https://github.com/wbuchwalter/tslint-loader#usage
 * @return {Function}
 */
function tslint (options) {
  options = options || {}

  const loader = (context, extra) => Object.assign({
    test: context.fileType('application/x-typescript'),
    loaders: [ 'tslint-loader' ]
  }, extra)

  const module = (context) => ({
    loaders: [ loader(context, { enforce: 'pre' }) ]
  })

  const setter = (context) => ({
    module: module(context),
    plugins: [
      new context.webpack.LoaderOptionsPlugin({
        options: {
          tslint: options
        }
      })
    ]
  })

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

function pre (context) {
  const registeredTypes = context.fileType.all()
  if (!('application/x-typescript' in registeredTypes)) {
    context.fileType.add('application/x-typescript', /\.(ts|tsx)$/)
  }
}

/**
 * Tslint webpack block.
 *
 * @see https://github.com/wbuchwalter/tslint-loader
 */

module.exports = tslint

/**
 * @param {object}   [options]                  See https://github.com/wbuchwalter/tslint-loader#usage
 * @return {Function}
 */
function tslint (options) {
  options = options || {}

  const setter = (context, helpers) => prevConfig => {
    const _addLoader = helpers.addLoader({
      test: context.fileType('application/x-typescript'),
      loaders: [ 'tslint-loader' ],
      enforce: 'pre'
    })
    const _addPlugin = helpers.addPlugin(
      new context.webpack.LoaderOptionsPlugin({
        options: {
          tslint: options
        }
      })
    )

    return _addLoader(_addPlugin(prevConfig))
  }

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

function pre (context) {
  const registeredTypes = context.fileType.all()

  if (!('application/x-typescript' in registeredTypes)) {
    context.fileType.add('application/x-typescript', /\.(ts|tsx)$/)
  }
}

Update tslint block to use new API


context('List View', () => {
	before(() => {
		cy.login('Administrator', 'qwe');
		cy.visit('/desk');
		cy.window().its('frappe').then(frappe => {
			frappe.call("frappe.tests.test_utils.setup_workflow");
			cy.reload();
		});
	});
	it('Bulk Workflow Action', () => {
		cy.go_to_list('ToDo');
		cy.get('.level-item.list-row-checkbox.hidden-xs').click({ multiple: true, force: true });
	});
});

context('List View', () => {
	before(() => {
		cy.login('Administrator', 'qwe');
		cy.visit('/desk');
		cy.window().its('frappe').then(frappe => {
			frappe.call("frappe.tests.test_utils.setup_workflow");
			cy.reload();
		});
	});
	it('enables "Actions" button', () => {
		const actions = ['Approve', 'Reject', 'Edit', 'Assign To', 'Print','Delete'];
		cy.go_to_list('ToDo');
		cy.get('.level-item.list-row-checkbox.hidden-xs').click({ multiple: true, force: true });
		cy.get('.btn.btn-primary.btn-sm.dropdown-toggle').contains('Actions').should('be.visible').click();
		cy.get('.dropdown-menu li:visible').should('have.length', 6).each((el, index) => {
			cy.wrap(el).contains(actions[index]);
		}).then((elements) => {
			cy.server();
			cy.route({
				method: 'POST',
				url:'api/method/frappe.model.workflow.bulk_workflow_approval'
			}).as('bulk-approval');
			cy.route({
				method: 'GET',
				url:'api/method/frappe.desk.reportview.get*'
			}).as('update-list');
			cy.wrap(elements).contains('Approve').click();
			cy.wait(['@bulk-approval', '@update-list']);
			cy.get('.list-row-container:visible').each(el => {
				cy.wrap(el).contains('Approved');
			});
		});
	});
});

Update list view UI test


// Use this file to change prototype configuration.

// Note: prototype config can be overridden using environment variables (eg on heroku)

module.exports = {
  // Service name used in header. Eg: 'Renew your passport'
  serviceName: '[Service Name]',

  // Default port that prototype runs on
  port: '3000',

  // Enable or disable password protection on production
  useAuth: 'true',

  // Automatically stores form data, and send to all views
  useAutoStoreData: 'true',

  // Enable or disable built-in docs and examples.
  useDocumentation: 'true',

  // Force HTTP to redirect to HTTPs on production
  useHttps: 'true',

  // Cookie warning - update link to service's cookie page.
  cookieText: 'GOV.UK uses cookies to make the site simpler. Find out more about cookies',

  // Enable or disable Browser Sync
  useBrowserSync: 'true'

}

// Use this file to change prototype configuration.

// Note: prototype config can be overridden using environment variables (eg on heroku)

module.exports = {
  // Service name used in header. Eg: 'Renew your passport'
  serviceName: 'Dart crossing charge',

  // Default port that prototype runs on
  port: '3000',

  // Enable or disable password protection on production
  useAuth: 'true',

  // Automatically stores form data, and send to all views
  useAutoStoreData: 'true',

  // Enable or disable built-in docs and examples.
  useDocumentation: 'true',

  // Force HTTP to redirect to HTTPs on production
  useHttps: 'true',

  // Cookie warning - update link to service's cookie page.
  cookieText: 'GOV.UK uses cookies to make the site simpler. Find out more about cookies',

  // Enable or disable Browser Sync
  useBrowserSync: 'true'

}

Update service name to Dart crossing charge for testing


scms.create('ProductVariationsViewBridge', function(){
    return {
        attachEvents:function () {
            var self = this;

            $(this.viewNode, '.product-variation-tab').click(function(event) {
                if (!event.target.classList.contains('fa')) {
                    self.changeTab($(this).parent());
                } else {
                    if (confirm('Are you sure you want to remove this variation?')) {
                        self.raiseServerEvent('VariationDelete', $(this).data('id'));
                    }
                }

            });
        },

        changeTab:function(tab){
            var lastSelected = $('.product-list-tabs.active a');

            this.raiseProgressiveServerEvent('changeVariation', lastSelected.data('id'), tab.data('id'));

            lastSelected.removeClass('active');
            tab.addClass('active');
        }
    };
});

scms.create('ProductVariationsViewBridge', function(){
    return {
        attachEvents:function () {
            var self = this;

            $('.product-variation-tab', this.viewNode).click(function(event) {
                if (!event.target.classList.contains('fa')) {
                    self.changeTab($(this).parent());
                } else {
                    if (confirm('Are you sure you want to remove this variation?')) {
                        self.raiseServerEvent('VariationDelete', $(this).data('id'));
                    }
                }

            });
        },

        changeTab:function(tab){
            var lastSelected = $('.product-list-tabs.active');

            this.raiseProgressiveServerEvent('changeVariation', lastSelected.data('id'), tab.data('id'));

            lastSelected.removeClass('active');
            tab.addClass('active');
        }
    };
});

Fix for active class not being set properly


var ContextElement = require('./contextElement')

module.exports = function (el, cb) {
	if ( ! (el instanceof ContextElement)) return cb('Given element is not a ContextElement.')

	var attributes = el.getMapping().attributes

	for (var i in attributes) {
		var attr = attributes[i]

		if ( ! Array.isArray(attr.metadatas)) continue

		var value = el.getAttributeValue(attr.name)

		attr.metadatas.forEach(function (meta) {
			if (meta.name === 'min' && meta.type === 'number') {
				value = Math.max(value, meta.value)
			} else if (meta.name === 'min' && meta.type === 'number') {
				value = Math.min(value, meta.value)
			}
		})

		el.setAttributeValue(attr.name, value)
	}

	cb()
}

var ContextElement = require('./contextElement')

var validators = {
	min: function (current, valid) { return Math.max(current, valid) },
	max: function (current, valid) { return Math.min(current, valid) },
}

module.exports = function (el, cb) {
	if ( ! (el instanceof ContextElement)) return cb('Given element is not a ContextElement.')

	var attributes = el.getMapping().attributes

	for (var i in attributes) {
		var attr = attributes[i]

		if ( ! Array.isArray(attr.metadatas)) continue

		var value = el.getAttributeValue(attr.name)

		attr.metadatas.forEach(function (meta) {
			var fn = validators[meta.name]
			if (typeof fn !== 'function') {
				console.error('No validator for meta', meta)
				return
			}
			value = fn(value, meta.value)
		})

		el.setAttributeValue(attr.name, value)
	}

	cb()
}

Make it easy to add validators.


const numberOfPlannedTests = 6
casper.test.begin('test-simple-image-resize', numberOfPlannedTests, (test) => {
  casper.start(`http://${testhost}`, function () {
  })

  casper.then(function () {
    const curr        = this.getCurrentUrl()
    const fixturePath = this.fetchText('#fixture_path')

    this.fill('#entryForm', {
      my_file     : `${fixturePath}/1.jpg`,
      width_field : '400',
      height_field: '400',
    })

    this.evaluate(function () { $('#entryForm').submit() })

    this.waitFor(function () {
      return curr !== this.getCurrentUrl()
    })
  })

  casper.then(function () {
    this.test.assertTextExists('ASSEMBLY_COMPLETED')
  })

  casper.run(function () {
    this.test.done()
  })
})

const numberOfPlannedTests = 5
casper.test.begin('test-simple-image-resize', numberOfPlannedTests, (test) => {
  casper.start(`http://${testhost}`, function () {
  })

  casper.then(function () {
    const curr        = this.getCurrentUrl()
    const fixturePath = this.fetchText('#fixture_path')

    this.fill('#entryForm', {
      my_file     : `${fixturePath}/1.jpg`,
      width_field : '400',
      height_field: '400',
    })

    this.evaluate(function () { $('#entryForm').submit() })

    this.waitFor(function () {
      return curr !== this.getCurrentUrl()
    })
  })

  casper.then(function () {
    this.test.assertTextExists('ASSEMBLY_COMPLETED')
  })

  casper.run(function () {
    this.test.done()
  })
})

Change num of planned tests.


/**
 *    _____ __
 *   / ___// /_  _____
 *   \__ \/ / / / / _ \
 *  ___/ / / /_/ /  __/
 * /____/_/\__, /\___/
 *       /____/
 *       Copyright 2017 Slye Development Team. All Rights Reserved.
 *       Licence: MIT License
 */

const tree      = require('./core/tree');
const compile   = require('./core/compile');
const modules   = require('./core/modules');
const blocks    = require('./core/blocks');
const configs   = require('./core/config');
const cache     = require('./core/cache');

var esy_lang  = {
	tree    : tree,
	compile : compile,
	cache   : cache,
	configs : configs,
	block   : blocks.add,
	modules : modules._esy(esy_lang)
};

module.exports = esy_lang;

/**
 *    _____ __
 *   / ___// /_  _____
 *   \__ \/ / / / / _ \
 *  ___/ / / /_/ /  __/
 * /____/_/\__, /\___/
 *       /____/
 *       Copyright 2017 Slye Development Team. All Rights Reserved.
 *       Licence: MIT License
 */

const tree      = require('./core/tree');
const compile   = require('./core/compile');
const modules   = require('./core/modules');
const blocks    = require('./core/blocks');
const configs   = require('./core/config');
const cache     = require('./core/cache');
const glob      = require('glob');
const path      = require('path');

var esy_lang  = {
	tree    : tree,
	compile : compile,
	cache   : cache,
	configs : configs,
	block   : blocks.add,
};
esy_lang['modules'] = modules._esy(esy_lang);

// Load modules
(function () {
	var modules = glob.sync('../modules/*/index.js', {cwd: __dirname}),
		len     = modules.length,
		i       = 0;
	for(;i < len;i++)
		esy_lang.modules.load(path.join(__dirname, modules[i]))
})();

module.exports = esy_lang;

Load modules from modules dir


// @flow

import { StyleSheet } from 'react-native'
import { ApplicationStyles, Metrics, Colors } from '../../Themes/'

export default StyleSheet.create({
  ...ApplicationStyles.screen,
  container: {
    flex: 1,
    marginTop: Metrics.navBarHeight,
    backgroundColor: Colors.background
  },
  row: {
    flex: 1,
    backgroundColor: Colors.fire,
    marginVertical: Metrics.smallMargin,
    justifyContent: 'center'
  },
  boldLabel: {
    fontWeight: 'bold',
    alignSelf: 'center',
    color: Colors.snow,
    textAlign: 'center',
    marginBottom: Metrics.smallMargin
  },
  label: {
    textAlign: 'center',
    color: Colors.snow
  },
  listContent: {
    marginTop: Metrics.baseMargin
  }
})

// @flow

import { StyleSheet } from 'react-native'
import { ApplicationStyles, Metrics, Colors } from '../../Themes/'

export default StyleSheet.create({
  ...ApplicationStyles.screen,
  container: {
    flex: 1,
    marginTop: Metrics.navBarHeight,
    backgroundColor: Colors.background
  },
  row: {
    flex: 1,
    backgroundColor: Colors.fire,
    marginVertical: Metrics.smallMargin,
    justifyContent: 'center'
  },
  boldLabel: {
    fontWeight: 'bold',
    alignSelf: 'center',
    color: Colors.snow,
    textAlign: 'center',
    marginVertical: Metrics.smallMargin
  },
  label: {
    textAlign: 'center',
    color: Colors.snow,
    marginBottom: Metrics.smallMargin
  },
  listContent: {
    marginTop: Metrics.baseMargin
  }
})

Modify listview styles for search listview and general margin


// Here be the site javascript!

$(document).ready(function () {
    $(".button-collapse").sideNav();
    $('.scrollspy').scrollSpy();
    $("form").submit(function (event) {
       event.preventDefault();
       $("#send-msg").attr("disabled", true);

       $("#fa-send").toggleClass("fa-envelope-o").toggleClass("fa-spinner").toggleClass("fa-spin");
       var msg      =  $("#message").val();
       var mail     = $("#email").val();
       var subject  = $("#subject").val();

        payload = {subject: subject, email: mail, message: msg};

        // Send mail to the local python mail server
        $.ajax({
            type:    "POST",
            url:     "http://46.101.248.58:5000/sendmail",
            crossDomain: true,
            data:    payload,
            complete: function (data, status, req) {
                $("#fa-send").toggleClass("fa-envelope-o").toggleClass("fa-spinner").toggleClass("fa-spin");

                $("#message").val("");
                $("#email").val("");
                $("#subject").val("");
                $("#send-msg").attr("disabled", false);
                $('#modal1').openModal();
            }
        });

    });
});

// Here be the site javascript!

$(document).ready(function () {
    $(".button-collapse").sideNav();
    $('.scrollspy').scrollSpy();
    $("form").submit(function (event) {
       event.preventDefault();
       $("#send-msg").attr("disabled", true);

       $("#fa-send").toggleClass("fa-envelope-o").toggleClass("fa-spinner").toggleClass("fa-spin");
       var msg      =  $("#message").val();
       var mail     = $("#email").val();
       var subject  = $("#subject").val();

        payload = {subject: subject, email: mail, message: msg};

        // Send mail to the local python mail server
        $.ajax({
            type:    "POST",
            url:     "http://mattij.com:5000/sendmail",
            crossDomain: true,
            data:    payload,
            complete: function (data, status, req) {
                $("#fa-send").toggleClass("fa-envelope-o").toggleClass("fa-spinner").toggleClass("fa-spin");

                $("#message").val("");
                $("#email").val("");
                $("#subject").val("");
                $("#send-msg").attr("disabled", false);
                $('#modal1').openModal();
            }
        });

    });
});

Add url to contact form