<commit_before>'use strict'

var os = require('os')

exports.default = {
  servers: {
    web: function (api) {
      return {
        enabled: true,
        // HTTP or HTTPS?
        secure: false,
        // Passed to https.createServer if secure=true. Should contain SSL certificates
        serverOptions: {},
        // Should we redirect all traffic to the first host in this array if hte request header doesn't match?
        // i.e.: [ 'https://www.site.com' ]
        allowedRequestHosts: process.env.ALLOWED_HOSTS ? process.env.ALLOWED_HOSTS.split(',') : [],
        // Port or Socket Path
        port: process.env.PORT || 5000,
        // Which IP to listen on (use '0.0.0.0' for all '::' for all on ipv4 and ipv6)
        // Set to `null` when listening to socket
        bindIP: '0.0.0.0',
        // Any additional headers you want actionhero to respond with
        httpHeaders: {
          'X-Powered-By': api.config.general.serverName,
          'Access-Control-Allow-Origin': 'http://192.168.0.105:9966',
          'Access-Control-Allow-Methods': 'HEAD, GET, POST, PUT, PATCH, DELETE, OPTIONS, TRACE',
          'Access-Control-Allow-Headers': 'Content-Type, X-SB-CSRF-Token, Language',
          'Access-Control-Allow-Credentials': 'true'
        },
        // Route that actions will be served from secondary route against this route will be treated as actions,
        //  IE: /api/?action=test == /api/test/
        urlPathForActions: 'api',
        // Route that static files will be served from
        //  path (relative to your project root) to serve static content from
        //  set to `null` to disable the file server entirely
        urlPathForFiles: 'public',
        // When visiting the root URL, should visitors see 'api' or 'file'?
        //  Visitors can always visit /api and /public as normal
        rootEndpointType: 'file',
        // simple routing also adds an 'all' route which matches /api/:action for all actions
        simpleRouting: false,
        // queryRouting allows an action to be defined via a URL param, ie: /api?action=:action
        queryRouting: false,
        // The header which will be returned for all flat file served from /public defined in seconds
        flatFileCacheDuration: 60,
        // Add an etag header to requested flat files which acts as fingerprint that changes when the file is updated;
        // Client will revalidate the fingerprint at latest after flatFileCacheDuration and reload it if the etag (and therfore the file) changed
        // or continue to use the cached file if it's still valid
        enableEtag: true,
        // How many times should we try to boot the server?
        // This might happen if the port is in use by another process or the socketfile is claimed
        bootAttempts: 1,
        // Settings for determining the id of an http(s) request (browser-fingerprint)
        fingerprintOptions: {
          cookieKey: 'sessionID',
          toSetCookie: true,
          onlyStaticElements: false,
          settings: {
            path: '/',
            expires: 3600000
          }
        },
        // Options to be applied to incoming file uploads.
        //  More options and details at https://github.com/felixge/node-formidable
        formOptions: {
          uploadDir: os.tmpdir(),
          keepExtensions: false,
          maxFieldsSize: 1024 * 1024 * 100
        },
        // Should we pad JSON responses with whitespace to make them more human-readable?
        // set to null to disable
        padding: 2,
        // Options to configure metadata in responses
        metadataOptions: {
          serverInformation: true,
          requesterInformation: true
        },
        // When true, returnErrorCodes will modify the response header for http(s) clients if connection.error is not null.
        //  You can also set connection.rawConnection.responseHttpCode to specify a code per request.
        returnErrorCodes: true,
        // should this node server attempt to gzip responses if the client can accept them?
        // this will slow down the performance of actionhero, and if you need this funcionality, it is recommended that you do this upstream with nginx or your load balancer
        compress: false,
        // options to pass to the query parser
        // learn more about the options @ https://github.com/hapijs/qs
        queryParseOptions: {}
      }
    }
  }
}

exports.staging = {
  servers: {
    web: function (api) {
      return {
        padding: null,
        httpHeaders: {
          'X-Powered-By': api.config.general.serverName,
          'Access-Control-Allow-Origin': 'http://sb.87.252.173.51.xip.io',
          'Access-Control-Allow-Methods': 'HEAD, GET, POST, PUT, PATCH, DELETE, OPTIONS, TRACE',
          'Access-Control-Allow-Headers': 'Content-Type, X-SB-CSRF-Token',
          'Access-Control-Allow-Credentials': 'true'
        }
      }
    }
  }
}

exports.production = {
  servers: {
    web: function (api) {
      return {
        padding: null,
        httpHeaders: {
          'X-Powered-By': api.config.general.serverName,
          'Access-Control-Allow-Origin': 'https://smartbirds.org',
          'Access-Control-Allow-Methods': 'HEAD, GET, POST, PUT, PATCH, DELETE, OPTIONS, TRACE',
          'Access-Control-Allow-Headers': 'Content-Type, X-SB-CSRF-Token',
          'Access-Control-Allow-Credentials': 'true'
        },
        metadataOptions: {
          serverInformation: false,
          requesterInformation: false
        }
      }
    }
  }
}

exports.test = {
  servers: {
    web: function (api) {
      return {
        secure: false,
        port: 1000 + (process.pid % 64535),
        matchExtensionMime: true,
        metadataOptions: {
          serverInformation: true,
          requesterInformation: true
        }
      }
    }
  }
}
<commit_msg>Remove change in web config<commit_after>'use strict'

var os = require('os')

exports.default = {
  servers: {
    web: function (api) {
      return {
        enabled: true,
        // HTTP or HTTPS?
        secure: false,
        // Passed to https.createServer if secure=true. Should contain SSL certificates
        serverOptions: {},
        // Should we redirect all traffic to the first host in this array if hte request header doesn't match?
        // i.e.: [ 'https://www.site.com' ]
        allowedRequestHosts: process.env.ALLOWED_HOSTS ? process.env.ALLOWED_HOSTS.split(',') : [],
        // Port or Socket Path
        port: process.env.PORT || 5000,
        // Which IP to listen on (use '0.0.0.0' for all '::' for all on ipv4 and ipv6)
        // Set to `null` when listening to socket
        bindIP: '0.0.0.0',
        // Any additional headers you want actionhero to respond with
        httpHeaders: {
          'X-Powered-By': api.config.general.serverName,
          'Access-Control-Allow-Origin': 'http://localhost:9966',
          'Access-Control-Allow-Methods': 'HEAD, GET, POST, PUT, PATCH, DELETE, OPTIONS, TRACE',
          'Access-Control-Allow-Headers': 'Content-Type, X-SB-CSRF-Token, Language',
          'Access-Control-Allow-Credentials': 'true'
        },
        // Route that actions will be served from secondary route against this route will be treated as actions,
        //  IE: /api/?action=test == /api/test/
        urlPathForActions: 'api',
        // Route that static files will be served from
        //  path (relative to your project root) to serve static content from
        //  set to `null` to disable the file server entirely
        urlPathForFiles: 'public',
        // When visiting the root URL, should visitors see 'api' or 'file'?
        //  Visitors can always visit /api and /public as normal
        rootEndpointType: 'file',
        // simple routing also adds an 'all' route which matches /api/:action for all actions
        simpleRouting: false,
        // queryRouting allows an action to be defined via a URL param, ie: /api?action=:action
        queryRouting: false,
        // The header which will be returned for all flat file served from /public defined in seconds
        flatFileCacheDuration: 60,
        // Add an etag header to requested flat files which acts as fingerprint that changes when the file is updated;
        // Client will revalidate the fingerprint at latest after flatFileCacheDuration and reload it if the etag (and therfore the file) changed
        // or continue to use the cached file if it's still valid
        enableEtag: true,
        // How many times should we try to boot the server?
        // This might happen if the port is in use by another process or the socketfile is claimed
        bootAttempts: 1,
        // Settings for determining the id of an http(s) request (browser-fingerprint)
        fingerprintOptions: {
          cookieKey: 'sessionID',
          toSetCookie: true,
          onlyStaticElements: false,
          settings: {
            path: '/',
            expires: 3600000
          }
        },
        // Options to be applied to incoming file uploads.
        //  More options and details at https://github.com/felixge/node-formidable
        formOptions: {
          uploadDir: os.tmpdir(),
          keepExtensions: false,
          maxFieldsSize: 1024 * 1024 * 100
        },
        // Should we pad JSON responses with whitespace to make them more human-readable?
        // set to null to disable
        padding: 2,
        // Options to configure metadata in responses
        metadataOptions: {
          serverInformation: true,
          requesterInformation: true
        },
        // When true, returnErrorCodes will modify the response header for http(s) clients if connection.error is not null.
        //  You can also set connection.rawConnection.responseHttpCode to specify a code per request.
        returnErrorCodes: true,
        // should this node server attempt to gzip responses if the client can accept them?
        // this will slow down the performance of actionhero, and if you need this funcionality, it is recommended that you do this upstream with nginx or your load balancer
        compress: false,
        // options to pass to the query parser
        // learn more about the options @ https://github.com/hapijs/qs
        queryParseOptions: {}
      }
    }
  }
}

exports.staging = {
  servers: {
    web: function (api) {
      return {
        padding: null,
        httpHeaders: {
          'X-Powered-By': api.config.general.serverName,
          'Access-Control-Allow-Origin': 'http://sb.87.252.173.51.xip.io',
          'Access-Control-Allow-Methods': 'HEAD, GET, POST, PUT, PATCH, DELETE, OPTIONS, TRACE',
          'Access-Control-Allow-Headers': 'Content-Type, X-SB-CSRF-Token',
          'Access-Control-Allow-Credentials': 'true'
        }
      }
    }
  }
}

exports.production = {
  servers: {
    web: function (api) {
      return {
        padding: null,
        httpHeaders: {
          'X-Powered-By': api.config.general.serverName,
          'Access-Control-Allow-Origin': 'https://smartbirds.org',
          'Access-Control-Allow-Methods': 'HEAD, GET, POST, PUT, PATCH, DELETE, OPTIONS, TRACE',
          'Access-Control-Allow-Headers': 'Content-Type, X-SB-CSRF-Token',
          'Access-Control-Allow-Credentials': 'true'
        },
        metadataOptions: {
          serverInformation: false,
          requesterInformation: false
        }
      }
    }
  }
}

exports.test = {
  servers: {
    web: function (api) {
      return {
        secure: false,
        port: 1000 + (process.pid % 64535),
        matchExtensionMime: true,
        metadataOptions: {
          serverInformation: true,
          requesterInformation: true
        }
      }
    }
  }
}
<|endoftext|>


<commit_before>function read(URL, callback) {
    /**
    reads the contents of the file at the URL,
    converts it into a string, and then
    calls the callback function (which has no arguments)
    with "this" equalling the responseText
    */
    var file = new XMLHttpRequest();
    file.open("GET", URL);  // Don't add false as an extra argument (browsers don't like it). (default: asynchronous=true)
    file.onreadystatechange = function () {
        if(file.readyState === 4) {  // Is it done?
            if(file.status === 200 || file.status == 0) {  // Was it successful?
                // file.responseXML might have something
                callback.call(file.responseText);  // .call(calling object / value of "this", function arguments (listed individually))  .apply has function arguments in an array
                // You could also use callback(argument(s)) like a normal function, but it wouldn't change the value of "this".
            }
        }
    }
    file.send();
}

function pageJump(ID) {
    /**
    makes a section to jump to certain parts of the page
    */
    document.getElementsByTagName("h1")[0].id = "top";
    var division = document.getElementById(ID);
    var contents = document.createElement("div");
    contents.className = "list";
    contents.innerHTML = "

Jump to:
";
    var sections = division.getElementsByTagName("h2");
    var toTop = document.createElement("a");
    toTop.href = "#top";
    toTop.innerHTML = "To top";
    var listItems = "";  // I need this variable to hold the list items because trying to add
 at the beginning and
 at the end separately causes it to fill in the
 immediately after the
 and omit the real

    for (var index=0; index<sections.length; index++) {  // I would use (var in array), but index exceeds entries.length for no apparent reason
        var inside = sections[index].innerHTML.trim();  // The inner HTML has a bunch of whitespace for no apparent reason.
        sections[index].id = inside;
        listItems += "
" + inside + "
";
        division.insertBefore(toTop.cloneNode(true), division.getElementsByTagName("h2")[index].nextSibling);  // inserts after

        // toTop needs to be cloned so it doesn't keep getting reasigned to the next place (it also needs to have true to clone all children of the node, although it doesn't apply here)
    }
    contents.innerHTML += "
" + listItems + "

";
    document.body.insertBefore(contents, division);  // .insertBefore() only works for the immediate descendants of the parent
}

//This is able to run without waiting for anything else to load.

// makes my custom tag which overlines things
document.createElement("over");

// adds the universal formatting
var style = document.createElement("link");
style.rel = "stylesheet";
style.href = "https://coolprogramminguser.github.io/Standards/formatting.css";
document.head.insertBefore(style, document.head.children[0]);

// links a favicon
var icon = document.createElement("link");
icon.rel = "icon";
var circleColors = ["Red", "Yellow", "Green", "Cyan", "Blue", "Magenta", "Black"];
icon.href = "favicons/" + circleColors[Math.floor(Math.random()*circleColors.length)] + " Circle.ico";
document.head.insertBefore(icon, document.head.children[0]);

window.addEventListener("load", function() {  // This waits for everything past the script import to load before running.

    // surrounds every list with

    var orderedLists = document.getElementsByTagName("ol");
    var unorderedLists = document.getElementsByTagName("ul");
    for (var index=0; index<orderedLists.length; index++) {
        orderedLists[index].outerHTML = "
" + orderedLists[index].outerHTML + "
";
    }
    for (var index=0; index<unorderedLists.length; index++) {
        unorderedLists[index].outerHTML = "
" + unorderedLists[index].outerHTML + "
";
    }
});
<commit_msg>added favicon cycling<commit_after>function read(URL, callback) {
    /**
    reads the contents of the file at the URL,
    converts it into a string, and then
    calls the callback function (which has no arguments)
    with "this" equalling the responseText
    */
    var file = new XMLHttpRequest();
    file.open("GET", URL);  // Don't add false as an extra argument (browsers don't like it). (default: asynchronous=true)
    file.onreadystatechange = function () {
        if(file.readyState === 4) {  // Is it done?
            if(file.status === 200 || file.status == 0) {  // Was it successful?
                // file.responseXML might have something
                callback.call(file.responseText);  // .call(calling object / value of "this", function arguments (listed individually))  .apply has function arguments in an array
                // You could also use callback(argument(s)) like a normal function, but it wouldn't change the value of "this".
            }
        }
    }
    file.send();
}

function pageJump(ID) {
    /**
    makes a section to jump to certain parts of the page
    */
    document.getElementsByTagName("h1")[0].id = "top";
    var division = document.getElementById(ID);
    var contents = document.createElement("div");
    contents.className = "list";
    contents.innerHTML = "

Jump to:
";
    var sections = division.getElementsByTagName("h2");
    var toTop = document.createElement("a");
    toTop.href = "#top";
    toTop.innerHTML = "To top";
    var listItems = "";  // I need this variable to hold the list items because trying to add
 at the beginning and
 at the end separately causes it to fill in the
 immediately after the
 and omit the real

    for (var index=0; index<sections.length; index++) {  // I would use (var in array), but index exceeds entries.length for no apparent reason
        var inside = sections[index].innerHTML.trim();  // The inner HTML has a bunch of whitespace for no apparent reason.
        sections[index].id = inside;
        listItems += "
" + inside + "
";
        division.insertBefore(toTop.cloneNode(true), division.getElementsByTagName("h2")[index].nextSibling);  // inserts after

        // toTop needs to be cloned so it doesn't keep getting reasigned to the next place (it also needs to have true to clone all children of the node, although it doesn't apply here)
    }
    contents.innerHTML += "
" + listItems + "

";
    document.body.insertBefore(contents, division);  // .insertBefore() only works for the immediate descendants of the parent
}

//This is able to run without waiting for anything else to load.

// makes my custom tag which overlines things
document.createElement("over");

// adds the universal formatting
var style = document.createElement("link");
style.rel = "stylesheet";
style.href = "https://coolprogramminguser.github.io/Standards/formatting.css";
document.head.insertBefore(style, document.head.children[0]);

// links a favicon
var faviconNumber = 1;
var icon = document.createElement("link");
icon.rel = "icon";
var circleColors = ["Red", "Yellow", "Green", "Cyan", "Blue", "Magenta"];
icon.href = "https://coolprogramminguser.github.io/Standards/favicons/Red Circle.ico";
document.head.insertBefore(icon, document.head.children[0]);

window.addEventListener("load", function() {  // This waits for everything past the script import to load before running.

    // surrounds every list with

    var orderedLists = document.getElementsByTagName("ol");
    var unorderedLists = document.getElementsByTagName("ul");
    for (var index=0; index<orderedLists.length; index++) {
        orderedLists[index].outerHTML = "
" + orderedLists[index].outerHTML + "
";
    }
    for (var index=0; index<unorderedLists.length; index++) {
        unorderedLists[index].outerHTML = "
" + unorderedLists[index].outerHTML + "
";
    }

    // cycles the favicon
    setInterval(function() {
        icon.href = "https://coolprogramminguser.github.io/Standards/favicons/" + circleColors[faviconNumber] + " Circle.ico";
        faviconNumber = faviconNumber<circleColors.length-1 ? faviconNumber+1 : 0;
    }, 1000);
});
<|endoftext|>


<commit_before>/*!
 * FileInput Ukrainian Translations
 *
 * This file must be loaded after 'fileinput.js'. Patterns in braces '{}', or
 * any HTML markup tags in the messages must not be converted or translated.
 *
 * @see http://github.com/kartik-v/bootstrap-fileinput
 * @author CyanoFresh <cyanofresh@gmail.com>
 *
 * NOTE: this file must be saved in UTF-8 encoding.
 */
(function ($) {
    "use strict";

    $.fn.fileinputLocales['uk'] = {
        fileSingle: 'файл',
        filePlural: 'файли',
        browseLabel: 'Вибрати …',
        removeLabel: 'Видалити',
        removeTitle: 'Видалити вибрані файли',
        cancelLabel: 'Скасувати',
        cancelTitle: 'Скасувати поточну загрузку',
        uploadLabel: 'Загрузити',
        uploadTitle: 'Загрузити вибрані файли',
        msgSizeTooLarge: 'Файл "{name}" ({size} KB) перевищує максимальний розмыр {maxSize} KB',
        msgFilesTooLess: 'Ви повинні вибрати як мінімум {n} {files} для загрузки',
        msgFilesTooMany: 'Кількість вибраних файлів ({n}) перевищує максимально допустиму кількість {m}',
        msgFileNotFound: 'Файл "{name}" не знайдено!',
        msgFileSecured: 'Обмеження безпеки перешкоджають читанню файла "{name}".',
        msgFileNotReadable: 'Файл "{name}" неможливо прочитати.',
        msgFilePreviewAborted: 'Перегляд скасований для файла "{name}".',
        msgFilePreviewError: 'Сталася помилка під час читання файла "{name}".',
        msgInvalidFileType: 'Заборонений тип файла для "{name}". Тільки "{types}" дозволені.',
        msgInvalidFileExtension: 'Заборонене розширення для файла "{name}". Тільки "{extensions}" дозволені.',
        msgValidationError: 'Помилка під час загрузки файла',
        msgLoading: 'Загрузка файла {index} із {files} …',
        msgProgress: 'Загрузка файла {index} із {files} - {name} - {percent}% завершено.',
        msgSelected: '{n} {files} вибрано',
        msgFoldersNotAllowed: 'Дозволено перетягувати тільки файли! Пропущено {n} папок.',
        msgImageWidthSmall: 'Ширина файлу зображення "{name}" повинна бути не менше {size} точок.',
        msgImageHeightSmall: 'Висота файлу зображення "{name}" повинна бути не менше {size} точок.',
        msgImageWidthLarge: 'Ширина файлу зображення "{name}", не може перевищувати {size} точок.',
        msgImageHeightLarge: 'Висота файлу зображення "{name}", не може перевищувати {size} точок.',
        dropZoneTitle: 'Перетяніть файли сюди …'
    };
})(window.jQuery);<commit_msg>Update fileinput_locale_uk.js<commit_after>/*!
 * FileInput Ukrainian Translations
 *
 * This file must be loaded after 'fileinput.js'. Patterns in braces '{}', or
 * any HTML markup tags in the messages must not be converted or translated.
 *
 * @see http://github.com/kartik-v/bootstrap-fileinput
 * @author CyanoFresh <cyanofresh@gmail.com>
 *
 * NOTE: this file must be saved in UTF-8 encoding.
 */
(function ($) {
    "use strict";

    $.fn.fileinputLocales['uk'] = {
        fileSingle: 'файл',
        filePlural: 'файли',
        browseLabel: 'Вибрати …',
        removeLabel: 'Видалити',
        removeTitle: 'Видалити вибрані файли',
        cancelLabel: 'Скасувати',
        cancelTitle: 'Скасувати поточну загрузку',
        uploadLabel: 'Загрузити',
        uploadTitle: 'Загрузити вибрані файли',
        msgSizeTooLarge: 'Файл "{name}" ({size} KB) перевищує максимальний розмір {maxSize} KB',
        msgFilesTooLess: 'Ви повинні вибрати як мінімум {n} {files} для загрузки',
        msgFilesTooMany: 'Кількість вибраних файлів ({n}) перевищує максимально допустиму кількість {m}',
        msgFileNotFound: 'Файл "{name}" не знайдено!',
        msgFileSecured: 'Обмеження безпеки перешкоджають читанню файла "{name}".',
        msgFileNotReadable: 'Файл "{name}" неможливо прочитати.',
        msgFilePreviewAborted: 'Перегляд скасований для файла "{name}".',
        msgFilePreviewError: 'Сталася помилка під час читання файла "{name}".',
        msgInvalidFileType: 'Заборонений тип файла для "{name}". Тільки "{types}" дозволені.',
        msgInvalidFileExtension: 'Заборонене розширення для файла "{name}". Тільки "{extensions}" дозволені.',
        msgValidationError: 'Помилка під час загрузки файла',
        msgLoading: 'Загрузка файла {index} із {files} …',
        msgProgress: 'Загрузка файла {index} із {files} - {name} - {percent}% завершено.',
        msgSelected: '{n} {files} вибрано',
        msgFoldersNotAllowed: 'Дозволено перетягувати тільки файли! Пропущено {n} папок.',
        msgImageWidthSmall: 'Ширина зображення "{name}" повинна бути не менше {size} px.',
        msgImageHeightSmall: 'Висота зображення "{name}" повинна бути не менше {size} px.',
        msgImageWidthLarge: 'Ширина зображення "{name}", не може перевищувати {size} px.',
        msgImageHeightLarge: 'Висота зображення "{name}", не може перевищувати {size} px.',
        dropZoneTitle: 'Перетягніть файли сюди …'
    };
})(window.jQuery);
<|endoftext|>


<commit_before>import {
  RESOLVE_CUSTOM_STATE_UPDATE,
  EMIT_QUEUE_EVENT,
  EMIT_TRIGGER_PHOTON_FUNCTION,
  EMIT_FORWARD_HTTP_REQUEST,
  EMIT_SEND_UNIFIED_COMMAND,
  EMIT_SEND_SPOTIFY_COMMAND
} from 'ducks/occurrences';
import {
  FETCH_UNIFIED_ID,
  FETCH_SPOTIFY_TOKEN,
  EMIT_REGISTER_SPOTIFY_CLIENT,
  EMIT_SPOTIFY_REFRESH_TOKEN_UPDATE,
  EMIT_REGISTER_SPOTIFY_TOKENS
} from 'ducks/devices';
import { handleAction } from 'utils';

import resolveQueuedEvents from './resolve-queued-events';
import triggerPhotonFunction from './trigger-photon-function';
import forwardHTTPRequest from './forward-http-request';
import fetchUnified from './fetch-unified';
import sendUnifiedCommand from './send-unified-command';
import fetchSpotifyToken from './fetch-spotify-token';
import sendSpotifyCommand from './send-spotify-command';

export default (store) => (next) => (action) => {
  const reducers = {
    [RESOLVE_CUSTOM_STATE_UPDATE]() {
      resolveQueuedEvents(action);
    },

    [EMIT_QUEUE_EVENT]: () => {
      resolveQueuedEvents(action);
    },

    [EMIT_TRIGGER_PHOTON_FUNCTION]() {
      triggerPhotonFunction(action, next);
    },

    [EMIT_FORWARD_HTTP_REQUEST]() {
      forwardHTTPRequest(action, next);
    },

    [FETCH_UNIFIED_ID]() {
      fetchUnified(action, next);
    },

    [EMIT_SEND_UNIFIED_COMMAND]() {
      sendUnifiedCommand(store, action, next);
    },

    [EMIT_REGISTER_SPOTIFY_CLIENT]() {
      next({ type: FETCH_SPOTIFY_TOKEN, code: action.code });
    },

    [FETCH_SPOTIFY_TOKEN]() {
      fetchSpotifyToken(store, action, next);
    },

    [EMIT_SPOTIFY_REFRESH_TOKEN_UPDATE]() {
      store.getState().meta.spotifyApi.refreshAccessToken().then(({ body }) => {
        store.dispatch({
          type: EMIT_REGISTER_SPOTIFY_TOKENS,
          spotifyAccessToken: body.access_token,
          spotifyTokenTimeLeft: body.expires_in
        });
      });
    },

    [EMIT_SEND_SPOTIFY_COMMAND]() {
      sendSpotifyCommand(store, action, next);
    }
  };

  handleAction(store, action, reducers);

  next(action);
};
<commit_msg>Log API errors<commit_after>import {
  RESOLVE_CUSTOM_STATE_UPDATE,
  EMIT_QUEUE_EVENT,
  EMIT_TRIGGER_PHOTON_FUNCTION,
  EMIT_FORWARD_HTTP_REQUEST,
  EMIT_SEND_UNIFIED_COMMAND,
  EMIT_SEND_SPOTIFY_COMMAND
} from 'ducks/occurrences';
import {
  FETCH_UNIFIED_ID,
  FETCH_SPOTIFY_TOKEN,
  EMIT_REGISTER_SPOTIFY_CLIENT,
  EMIT_SPOTIFY_REFRESH_TOKEN_UPDATE,
  EMIT_REGISTER_SPOTIFY_TOKENS
} from 'ducks/devices';
import { handleAction, logger } from 'utils';

import resolveQueuedEvents from './resolve-queued-events';
import triggerPhotonFunction from './trigger-photon-function';
import forwardHTTPRequest from './forward-http-request';
import fetchUnified from './fetch-unified';
import sendUnifiedCommand from './send-unified-command';
import fetchSpotifyToken from './fetch-spotify-token';
import sendSpotifyCommand from './send-spotify-command';

export default (store) => (next) => (action) => {
  const reducers = {
    [RESOLVE_CUSTOM_STATE_UPDATE]() {
      resolveQueuedEvents(action);
    },

    [EMIT_QUEUE_EVENT]: () => {
      resolveQueuedEvents(action);
    },

    [EMIT_TRIGGER_PHOTON_FUNCTION]() {
      triggerPhotonFunction(action, next);
    },

    [EMIT_FORWARD_HTTP_REQUEST]() {
      forwardHTTPRequest(action, next);
    },

    [FETCH_UNIFIED_ID]() {
      fetchUnified(action, next);
    },

    [EMIT_SEND_UNIFIED_COMMAND]() {
      sendUnifiedCommand(store, action, next);
    },

    [EMIT_REGISTER_SPOTIFY_CLIENT]() {
      next({ type: FETCH_SPOTIFY_TOKEN, code: action.code });
    },

    [FETCH_SPOTIFY_TOKEN]() {
      fetchSpotifyToken(store, action, next);
    },

    [EMIT_SPOTIFY_REFRESH_TOKEN_UPDATE]() {
      store.getState().meta.spotifyApi.refreshAccessToken().then(({ body }) => {
        store.dispatch({
          type: EMIT_REGISTER_SPOTIFY_TOKENS,
          spotifyAccessToken: body.access_token,
          spotifyTokenTimeLeft: body.expires_in
        });
      });
    },

    [EMIT_SEND_SPOTIFY_COMMAND]() {
      sendSpotifyCommand(store, action, next);
    }
  };

  try {
    handleAction(store, action, reducers);
  } catch (e) {
    logger.log(`Problem with ${action}`, e);
  }

  next(action);
};
<|endoftext|>


<commit_before>Meteor.methods({
  'getResidentLatestActivityByType': function (residentId, activityTypeId) {
    /*
    Get the resident's most recent activity by type
    */
    var query = {
      activityTypeId: activityTypeId,
      residentIds: residentId
    };

    // Set up sort by activity date, in descending order
    var sort = {sort: {activityDate: -1}};

    // Get resident latest activity by type
    var residentLatestActivityByType = Activities.findOne(query, sort);

    // Return activity, if exists
    if (residentLatestActivityByType) {
      return residentLatestActivityByType;
    }
  },
   'getAllResidentsLatestActivitiesByType': function () {
    /*
    Return an array of Activity IDs
    where each activity type has at most one entry per resident
    containing the most recent activity of that type/resident
    */
    // Get all resident IDs
    var residentIds = Meteor.call('getAllResidentIds');

    // Get all Activity Type IDs
    var activityTypeIds = Meteor.call('getAllActivityTypeIds');

    // Placeholder for residents latest activity
    var residentsLatestActivitiesByType = [];

    // Iterate through resident IDs
    residentIds.forEach(function (residentId) {
      // for each resident ID
      // Iterate through activity types
      console.log(residentId);
      activityTypeIds.forEach(function (activityTypeId) {
        // Get a reference to Resident ID (as array-like object apparently)
        var residentIdObject = this;

        // Convert the resident ID array-like object to an array
        var residentIdArray = Array.prototype.slice.call(residentIdObject);

        // Convert the resident ID array to a string
        var residentId = residentIdArray.join("");

        // TODO: see if there is a simple way to accept a callback argument as string

        // for each activity type,
        // get resident latest activity
        var residentLatestActivityByType = Meteor.call('getResidentLatestActivityByType', residentId, activityTypeId);

        // append activity ID to residentsLatestActivitiesByType
        residentsLatestActivitiesByType.push(residentLatestActivityByType);
      }, residentId);
    });
    console.log(residentsLatestActivitiesByType);
    return residentsLatestActivitiesByType
  }
});
<commit_msg>Return array of activity IDs; cleanup<commit_after>Meteor.methods({
  'getResidentLatestActivityByType': function (residentId, activityTypeId) {
    /*
    Get the resident's most recent activity by type
    */
    var query = {
      activityTypeId: activityTypeId,
      residentIds: residentId
    };

    // Set up sort by activity date, in descending order
    var sort = {sort: {activityDate: -1}};

    // Get resident latest activity by type
    var residentLatestActivityByType = Activities.findOne(query, sort);

    // Return activity, if exists
    if (residentLatestActivityByType) {
      return residentLatestActivityByType;
    }
  },
   'getAllResidentsLatestActivityIdsByType': function () {
    /*
    Return an array of Activity IDs
    where each activity type has at most one entry per resident
    containing the most recent activity of that type/resident
    */
    // Get all resident IDs
    var residentIds = Meteor.call('getAllResidentIds');

    // Get all Activity Type IDs
    var activityTypeIds = Meteor.call('getAllActivityTypeIds');

    // Placeholder for residents latest activity
    var residentsLatestActivityIdsByType = [];

    // Iterate through resident IDs
    residentIds.forEach(function (residentId) {
      // for each resident ID
      // Iterate through activity types
      activityTypeIds.forEach(function (activityTypeId) {
        // Get a reference to Resident ID (as array-like object apparently)
        var residentIdObject = this;

        // Convert the resident ID array-like object to an array
        var residentIdArray = Array.prototype.slice.call(residentIdObject);

        // Convert the resident ID array to a string
        var residentId = residentIdArray.join("");

        // TODO: see if there is a simple way to accept a callback argument as string

        // for each activity type,
        // get resident latest activity
        var residentLatestActivityIdByType = Meteor.call('getResidentLatestActivityByType', residentId, activityTypeId);

        // append activity ID to residentsLatestActivityIdsByType
        residentsLatestActivityIdsByType.push(residentLatestActivityIdByType._id);
      }, residentId);
    });

    return residentsLatestActivityIdsByType
  }
});
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2015 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

CLASS({
  package: 'foam.build',
  name: 'BuildApp',

  imports: [
    'log',
    'error'
  ],
  requires: [
    'foam.dao.NodeFileDAO as FileDAO',
    'foam.dao.File',
    'foam.core.dao.OrDAO',
    'node.dao.ModelFileDAO'
  ],
  properties: [
    {
      name: 'controller',
      help: 'Name of the main controller/model to create',
      required: true
    },
    {
      model_: 'StringArrayProperty',
      name: 'coreFiles',
      adapt: function(_, s) { if ( typeof s === 'string' ) return s.split(','); return s; }
    },
    {
      name: 'defaultView',
      help: "Default view of the controller to use.  If not set, the controller will be used as view (if it is one), or DetailView will be used"
    },
    {
      name: 'targetPath',
      help: "Directory to write output files to.  Will be created if it doesn't exist.",
      required: true
    },
    {
      model_: 'BooleanProperty',
      name: 'precompileTemplates',
      help: 'True to precompile templates of models loaded from the ModelDAO.',
      defaultValue: false
    },
    {
      model_: 'StringArrayProperty',
      name: 'extraFiles',
      help: 'Extra files to both load during the build process, and include in the built image.',
      adapt: function(_, s) { if ( typeof s === 'string' ) return s.split(','); return s; }
    },
    {
      model_: 'StringArrayProperty',
      name: 'extraModels',
      help: 'Extra models to include in the image regardless of if they were arequired or not.',
      adapt: function(_, s) { if ( typeof s === 'string' ) return s.split(','); return s; },
      factory: function() { return ['foam.ui.FoamTagView']; }
    },
    {
      model_: 'StringArrayProperty',
      name: 'blacklistModels',
      help: 'Models to unconditionally exclude from the image, even if they are listed as required.',
      adapt: function(_, s) { if ( typeof s === 'string' ) return s.split(','); return s; }
    },
    {
      model_: 'BooleanProperty',
      name: 'outputManifest',
      defaultValue: false,
      help: 'Set to true to write out a MANIFEST file listing all included models.'
    },
    {
      name: 'htmlFileName',
      help: 'Name of the main html file to produce.',
      defaultValue: 'main.html'
    },
    {
      name: 'formatter',
      factory: function() {
        return {
          __proto__: JSONUtil.compact,
          keys_: {},
          keyify: JSONUtil.prettyModel.keyify,
          outputObject_: function(out, obj, opt_defaultModel) {
            var first = true;

            out('{');
            if ( obj.model_.id !== opt_defaultModel ) {
              this.outputModel_(out, obj);
              first = false;
            }

            if ( Template.isInstance(obj) ) var isTemplate = true;

            for ( var key in obj.model_.properties_ ) {
              var prop = obj.model_.properties_[key];

              if ( ! this.p(prop) && ( ! isTemplate || prop.name !== 'code' ) ) continue;

              if ( prop.name === 'documentation' ) continue;

              if ( prop.name in obj.instance_ ) {
                var val = obj[prop.name];
                if ( Array.isArray(val) && ! val.length ) continue;
                if ( ! first ) out(',');
                out(this.keyify(prop.name), ': ');

                if ( prop.name === 'methods' ) {
                  out('{');
                  var ff = true;
                  for ( var i = 0 ; i < val.length ; i++ ) {
                    if ( ! ff ) out(',');

                    out(this.keyify(val[i].name), ': ');
                    out(val[i].code);
                    ff = false;
                  }
                  out('}');
                } else {
                  this.output(out, val);
                }
                first = false;
              }
            }

            out('}');
          }
        };
      }
    },
    {
      name: 'path',
      factory: function() { return require('path'); }
    },
    {
      name: 'fileDAO',
      factory: function() { return this.FileDAO.create(); }
    },
    {
      model_: 'StringArrayProperty',
      name: 'extraClassPaths',
      help: 'List of extra .js hierarchies to load models from.  Paths will be checked in the order given, finally falling back to the main FOAM js/ hierarchy.',
      adapt: function(_, s) { if ( typeof s === 'string' ) return s.split(','); return s; }
    }
  ],
  methods: {
    execute: function() {
      if ( ! this.targetPath ) {
        this.error("targetPath is required");
        process.exit(1);
      }
      if ( ! this.controller ) {
        this.error("controller is required");
        process.exit(1);
      }

      for ( var i = this.extraClassPaths.length - 1; i >= 0; i-- ) {
        this.X.ModelDAO = this.OrDAO.create({
          primary: this.ModelFileDAO.create({
            classpath: this.extraClassPaths[i]
          }),
          delegate: this.X.ModelDAO
        });
      }

      for ( var i = 0 ; i < this.extraFiles.length ; i++ ) {
        require(FOAM_BOOT_DIR + this.path.sep + this.extraFiles[i] + '.js');
      }

      var view = this.defaultView ? arequire(this.defaultView) : anop;

      var seq = [view];
      for ( var i = 0; i < this.extraModels.length ; i++ ) {
        seq.push(arequire(this.extraModels[i]));
      }

      aseq(
        aseq.apply(null, seq),
        arequire(this.controller))(this.execute_.bind(this));
    },
    buildCoreJS_: function(ret) {
      var i = 0;
      var self = this;
      var corejs = '';
      var file;
      if ( this.coreFiles.length ) var myfiles = this.coreFiles;
      else myfiles = files;
      myfiles = myfiles.concat(this.extraFiles);
      awhile(
        function() { return i < myfiles.length; },
        aif(
          function() {
            file = myfiles[i++];
            if ( Array.isArray(file) ) {
              if ( file[1] != IN_BROWSER ) return false;
              file = file[0];
            }
            return true;
          },
          aseq(
            function(ret) {
              var path = FOAM_BOOT_DIR + this.path.sep + file + '.js';
              this.fileDAO.find(path, {
                put: ret,
                error: function() {
                  self.error.apply(["Error reading file: ", path].concat(arguments));
                }
              });
            }.bind(this),
            function(ret, file) {
              corejs += '\n';
              corejs += file.contents;
              ret();
            })))(function() { ret(corejs); });
    },
    buildAppJS_: function(ret) {
      var models = {};
      var visited = {};
      var error = this.error;
      var self = this;

      function add(require) {
        if ( visited[require] ) return;
        visited[require] = true;

        var model = X.lookup(require);
        if ( ! model ) {
          error("Could not load model: ", require);
        }
        if ( model.package &&
             self.blacklistModels.indexOf(model.id) == -1 ) {
          models[model.id] = model;
        }

        model.getAllRequires().forEach(add);
      };
      add(this.controller);
      if ( this.defaultView ) add(this.defaultView);

      for ( var i = 0; i < this.extraModels.length ; i++ ) {
        add(this.extraModels[i]);
      }

      var contents = '';

      var ids = Object.keys(models);
      if ( this.outputManifest ) {
        this.fileDAO.put(this.File.create({
          path: this.targetPath + this.path.sep + 'MANIFEST',
          contents: ids.join('\n')
        }));
      }

      for ( var i = 0; i < ids.length; i++ ) {
        var model = models[ids[i]];
        if ( this.precompileTemplates ) {
          for ( var j = 0 ; j < model.templates.length ; j++ ) {
            model.templates[j].code = TemplateUtil.compile(model.templates[j]);
            model.templates[j].clearProperty('template');
          }
        }
        contents += 'CLASS(';
        if ( this.precompileTemplates )
          contents += this.formatter.where(NOT_TRANSIENT).stringify(models[ids[i]]);
        else
          contents += JSONUtil.compact.where(NOT_TRANSIENT).stringify(models[ids[i]]);
        contents += ')\n';
      }

      ret(contents);
    },
    execute_: function(model) {
      if ( ! model ) {
        this.error("Could not find model: ", this.controller);
      }
      this.log("Building   ", model.id);
      this.log("Target is: ", this.targetPath);
      this.log(this.precompileTemplates ? '' : 'NOT ', 'pre-compiling templates.');

      var self = this;
      aseq(
        function(ret) {
          var file = this.File.create({
            path: this.targetPath + this.path.sep + this.htmlFileName,
            contents: this.HTML()
          });

          console.log("Writing: ", file.path);
          this.fileDAO.put(file, {
            put: ret,
            error: function() {
              self.error('ERROR writing file: ', file.path);
              process.exit(1);
            }
          });
        }.bind(this),
        apar(
          function(ret) { this.buildCoreJS_(ret); }.bind(this),
          function(ret) { this.buildAppJS_(ret); }.bind(this)),
        function(ret, corejs, appjs) {
          var file = this.File.create({
            path: this.targetPath + this.path.sep + 'foam.js',
            contents: corejs + appjs
          });
          console.log("Writing: ", file.path);
          this.fileDAO.put(file, {
            put: ret,
            error: function() {
              self.error("ERROR writing file: ", file.path);
              process.exit(1);
            }
          });
        }.bind(this)
      )(
        function(){
          process.exit(0);
        });
    }
  },
  templates: [
    function HTML() {/*<meta charset="utf-8"><foam model="<%= this.controller %>"<% if ( this.defaultView ) { %> view="<%= this.defaultView %>"<% } %>></foam>*/}
  ]
});
<commit_msg>BuildApp uses stringifyObject to avoid outputting redundant model_ info.<commit_after>/**
 * @license
 * Copyright 2015 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

CLASS({
  package: 'foam.build',
  name: 'BuildApp',

  imports: [
    'log',
    'error'
  ],
  requires: [
    'foam.dao.NodeFileDAO as FileDAO',
    'foam.dao.File',
    'foam.core.dao.OrDAO',
    'node.dao.ModelFileDAO'
  ],
  properties: [
    {
      name: 'controller',
      help: 'Name of the main controller/model to create',
      required: true
    },
    {
      model_: 'StringArrayProperty',
      name: 'coreFiles',
      adapt: function(_, s) { if ( typeof s === 'string' ) return s.split(','); return s; }
    },
    {
      name: 'defaultView',
      help: "Default view of the controller to use.  If not set, the controller will be used as view (if it is one), or DetailView will be used"
    },
    {
      name: 'targetPath',
      help: "Directory to write output files to.  Will be created if it doesn't exist.",
      required: true
    },
    {
      model_: 'BooleanProperty',
      name: 'precompileTemplates',
      help: 'True to precompile templates of models loaded from the ModelDAO.',
      defaultValue: false
    },
    {
      model_: 'StringArrayProperty',
      name: 'extraFiles',
      help: 'Extra files to both load during the build process, and include in the built image.',
      adapt: function(_, s) { if ( typeof s === 'string' ) return s.split(','); return s; }
    },
    {
      model_: 'StringArrayProperty',
      name: 'extraModels',
      help: 'Extra models to include in the image regardless of if they were arequired or not.',
      adapt: function(_, s) { if ( typeof s === 'string' ) return s.split(','); return s; },
      factory: function() { return ['foam.ui.FoamTagView']; }
    },
    {
      model_: 'StringArrayProperty',
      name: 'blacklistModels',
      help: 'Models to unconditionally exclude from the image, even if they are listed as required.',
      adapt: function(_, s) { if ( typeof s === 'string' ) return s.split(','); return s; }
    },
    {
      model_: 'BooleanProperty',
      name: 'outputManifest',
      defaultValue: false,
      help: 'Set to true to write out a MANIFEST file listing all included models.'
    },
    {
      name: 'htmlFileName',
      help: 'Name of the main html file to produce.',
      defaultValue: 'main.html'
    },
    {
      name: 'formatter',
      factory: function() {
        return {
          __proto__: JSONUtil.compact,
          keys_: {},
          keyify: JSONUtil.prettyModel.keyify,
          outputObject_: function(out, obj, opt_defaultModel) {
            var first = true;

            out('{');
            if ( obj.model_.id !== opt_defaultModel ) {
              this.outputModel_(out, obj);
              first = false;
            }

            if ( Template.isInstance(obj) ) var isTemplate = true;

            for ( var key in obj.model_.properties_ ) {
              var prop = obj.model_.properties_[key];

              if ( ! this.p(prop) && ( ! isTemplate || prop.name !== 'code' ) ) continue;

              if ( prop.name === 'documentation' ) continue;

              if ( prop.name in obj.instance_ ) {
                var val = obj[prop.name];
                if ( Array.isArray(val) && ! val.length ) continue;
                if ( ! first ) out(',');
                out(this.keyify(prop.name), ': ');

                if ( prop.name === 'methods' ) {
                  out('{');
                  var ff = true;
                  for ( var i = 0 ; i < val.length ; i++ ) {
                    if ( ! ff ) out(',');

                    out(this.keyify(val[i].name), ': ');
                    out(val[i].code);
                    ff = false;
                  }
                  out('}');
                } else {
                  this.output(out, val);
                }
                first = false;
              }
            }

            out('}');
          }
        };
      }
    },
    {
      name: 'path',
      factory: function() { return require('path'); }
    },
    {
      name: 'fileDAO',
      factory: function() { return this.FileDAO.create(); }
    },
    {
      model_: 'StringArrayProperty',
      name: 'extraClassPaths',
      help: 'List of extra .js hierarchies to load models from.  Paths will be checked in the order given, finally falling back to the main FOAM js/ hierarchy.',
      adapt: function(_, s) { if ( typeof s === 'string' ) return s.split(','); return s; }
    }
  ],
  methods: {
    execute: function() {
      if ( ! this.targetPath ) {
        this.error("targetPath is required");
        process.exit(1);
      }
      if ( ! this.controller ) {
        this.error("controller is required");
        process.exit(1);
      }

      for ( var i = this.extraClassPaths.length - 1; i >= 0; i-- ) {
        this.X.ModelDAO = this.OrDAO.create({
          primary: this.ModelFileDAO.create({
            classpath: this.extraClassPaths[i]
          }),
          delegate: this.X.ModelDAO
        });
      }

      for ( var i = 0 ; i < this.extraFiles.length ; i++ ) {
        require(FOAM_BOOT_DIR + this.path.sep + this.extraFiles[i] + '.js');
      }

      var view = this.defaultView ? arequire(this.defaultView) : anop;

      var seq = [view];
      for ( var i = 0; i < this.extraModels.length ; i++ ) {
        seq.push(arequire(this.extraModels[i]));
      }

      aseq(
        aseq.apply(null, seq),
        arequire(this.controller))(this.execute_.bind(this));
    },
    buildCoreJS_: function(ret) {
      var i = 0;
      var self = this;
      var corejs = '';
      var file;
      if ( this.coreFiles.length ) var myfiles = this.coreFiles;
      else myfiles = files;
      myfiles = myfiles.concat(this.extraFiles);
      awhile(
        function() { return i < myfiles.length; },
        aif(
          function() {
            file = myfiles[i++];
            if ( Array.isArray(file) ) {
              if ( file[1] != IN_BROWSER ) return false;
              file = file[0];
            }
            return true;
          },
          aseq(
            function(ret) {
              var path = FOAM_BOOT_DIR + this.path.sep + file + '.js';
              this.fileDAO.find(path, {
                put: ret,
                error: function() {
                  self.error.apply(["Error reading file: ", path].concat(arguments));
                }
              });
            }.bind(this),
            function(ret, file) {
              corejs += '\n';
              corejs += file.contents;
              ret();
            })))(function() { ret(corejs); });
    },
    buildAppJS_: function(ret) {
      var models = {};
      var visited = {};
      var error = this.error;
      var self = this;

      function add(require) {
        if ( visited[require] ) return;
        visited[require] = true;

        var model = X.lookup(require);
        if ( ! model ) {
          error("Could not load model: ", require);
        }
        if ( model.package &&
             self.blacklistModels.indexOf(model.id) == -1 ) {
          models[model.id] = model;
        }

        model.getAllRequires().forEach(add);
      };
      add(this.controller);
      if ( this.defaultView ) add(this.defaultView);

      for ( var i = 0; i < this.extraModels.length ; i++ ) {
        add(this.extraModels[i]);
      }

      var contents = '';

      var ids = Object.keys(models);
      if ( this.outputManifest ) {
        this.fileDAO.put(this.File.create({
          path: this.targetPath + this.path.sep + 'MANIFEST',
          contents: ids.join('\n')
        }));
      }

      for ( var i = 0; i < ids.length; i++ ) {
        var model = models[ids[i]];
        if ( this.precompileTemplates ) {
          for ( var j = 0 ; j < model.templates.length ; j++ ) {
            model.templates[j].code = TemplateUtil.compile(model.templates[j]);
            model.templates[j].clearProperty('template');
          }
        }
        contents += 'CLASS(';
        var formatter = this.precompileTemplates ? this.formatter : JSONUtil.compact;
        contents += formatter.where(NOT_TRANSIENT).stringifyObject(models[ids[i]], 'Model');
        contents += ')\n';
      }

      ret(contents);
    },
    execute_: function(model) {
      if ( ! model ) {
        this.error('Could not find model: ', this.controller);
      }
      this.log('Building   ', model.id);
      this.log('Target is: ', this.targetPath);
      this.log(this.precompileTemplates ? '' : 'NOT ', 'pre-compiling templates.');

      var self = this;
      aseq(
        function(ret) {
          var file = this.File.create({
            path: this.targetPath + this.path.sep + this.htmlFileName,
            contents: this.HTML()
          });

          console.log('Writing: ', file.path);
          this.fileDAO.put(file, {
            put: ret,
            error: function() {
              self.error('ERROR writing file: ', file.path);
              process.exit(1);
            }
          });
        }.bind(this),
        apar(
          function(ret) { this.buildCoreJS_(ret); }.bind(this),
          function(ret) { this.buildAppJS_(ret); }.bind(this)),
        function(ret, corejs, appjs) {
          var file = this.File.create({
            path: this.targetPath + this.path.sep + 'foam.js',
            contents: corejs + appjs
          });
          console.log('Writing: ', file.path);
          this.fileDAO.put(file, {
            put: ret,
            error: function() {
              self.error('ERROR writing file: ', file.path);
              process.exit(1);
            }
          });
        }.bind(this)
      )(
        function(){
          process.exit(0);
        });
    }
  },
  templates: [
    function HTML() {/*<meta charset="utf-8"><foam model="<%= this.controller %>"<% if ( this.defaultView ) { %> view="<%= this.defaultView %>"<% } %>></foam>*/}
  ]
});
<|endoftext|>


<commit_before>
//----------------------obtains data by right clicking on cluster or point
$( '#map' ).on( "click", '.obtain_data_from_cluster_or_marker', function( event )
{
    var geohashes = $(this).data("geohashes");
    var eid_count = $(this).data("eid_count");
    map.closePopup();

    retrieve_data('none', geohashes, eid_count);

});
//----------------------obtains data by right clicking on cluster or point

//-----------------------------------------------apply filter
$( "#apply_filter" ).click(function()
{
	map.closePopup();
    var crop_id   = $("#crop_filter").val();
    obtain_specific_crop_map_population(crop_id);

});
//-----------------------------------------------apply filter

//----------------------------------generate saved_data row from a current data row
function generate_saved_data_row_from_current_data_row(selected_row, current_id)
{
    var local_crid;
    var local_pdate;
    var local_soil_texture;
    var local_institution;
    var local_country;
    var local_exname;
    var local_agmip_rating;
    var local_eid;

    local_crid          = $(selected_row).find("[data-type='crid']").text();
    local_pdate         = $(selected_row).find("[data-type='pdate']").text();
    local_soil_texture  = $(selected_row).find("[data-type='soil']").text();
    local_institution   = $(selected_row).find("[data-type='institution']").text();
    local_country       = $(selected_row).find("[data-type='country']").text();
    local_exname        = $(selected_row).find("[data-type='exname']").text();
    local_agmip_rating  = $(selected_row).find("[data-type='rating']").text();
    local_eid           = $(selected_row).find("[data-type='eid']").text();

    var row     =   "" + "<input class='saved_data_selector' type='checkbox' >" + "	";

    row = row + ""+         local_crid  +"	";
    row = row + ""+        local_pdate +"	";
    row = row + ""+         local_soil_texture +"	";
    row = row + ""+  local_institution  +"	";
    row = row + ""+      local_country  +"	";
    row = row + ""+       local_exname +"	";
    row = row + ""+       local_agmip_rating +"	";

    row = "
"+ row +"
";

    //appends row after the last row
    $("#saved_data").find("table tr:last").after(row)
}
//----------------------------------generate saved_data row from a current data row

//----------------------actions when user clicks on the current data selector
$( '#current_data' ).on( "click", '.current_data_selector', function( event )
{
        var selected_row    = $(this).parent().parent();
        /*
            when the user clicks on the current data selector,
            this function finds the id of the selected row and does the following:
            (a) adds it to the saved data array if it is not there
            (b) removes it from the saved data array if it is there
        */
        //current id represents the eid, the id is stored as data attribute in the row
        var current_id      = selected_row.data('id');
        var index           = saved_data.indexOf(current_id);

        //--------------------------------controls saved_data
        if(index == -1)
        {
            //add eid to array if eid is not in array already
            saved_data.push( current_id );

            //-----------------appends rows to saved table
            generate_saved_data_row_from_current_data_row(selected_row, current_id);
            //-----------------appends rows to saved table
        }
        else
        {
            //remove eid from array
            saved_data.splice(index, 1);
            $("#saved_data").find("[data-id='"+current_id+"']").remove();
        }
        //--------------------------------controls saved_data

        show_hide_saved_data_table();

});
//----------------------actions when user clicks on the current data selector

//--------------------------------------------obtains data by clicking obtain data button
/*
    This function obtains data from database and builds current data table
*/
$( "#obtain_data" ).click(function() {

    var crop_id     = $("#crop_filter").val();
    var geohashes   = [];
    var map_bounds  = map.getBounds();
    var eid_count   = 0;
    var marker;

    //---selects geohashes if marker is within the bounds of view
    for (var i=0; i < markers.length; i++)
    {
        marker = markers[i];
        if( map_bounds.contains([parseFloat(marker['_latlng']["lat"]) ,parseFloat(marker['_latlng']["lng"])]) )
        {
            geohashes.push(marker['options']['geohash']);
            eid_count = eid_count + parseFloat(marker['options']['count']);
        }
    }
    //---selects geohashes if marker is within the bounds of view

    //retrieve_data triggers the build table function
    retrieve_data(crop_id, geohashes, eid_count);

});
//--------------------------------------------obtains data by clicking obtain data button

//--------------------------------------------------------when user hits checkbox
$( '#current_data' ).on( "click", '#select_all_current_data', function()
{

	if( $(this).prop('checked')  )
	{
		select_all_current_data();
	}
	else
	{
		deselect_all_current_data();
	}

});
//--------------------------------------------------------when user hits checkbox

//----------------------------------------select all current data
function select_all_current_data()
{
    var rows = $("#current_data table").find("tr");
    var selected_row;
    var current_id;
    var index;

    var name;
    var institution;
    var row;

    $(rows).each(function(i, value)
    {

        if(i > 0) //the first row is the title for columns, so skip it
        {

            selected_row    = rows[i];
            current_id      = $(selected_row).data('id');

            index           = saved_data.indexOf(current_id);

            if(index == -1)
            {
                $(selected_row).find(".current_data_selector").prop('checked', true);

                //add eid to array if eid is not in array already
                saved_data.push( current_id );

                //-----------------appends rows to saved table
                generate_saved_data_row_from_current_data_row(selected_row, current_id);
                //-----------------appends rows to saved table
            }

            show_hide_saved_data_table();

        }

    });

}
//----------------------------------------select all current data

//--------------------------------deselect all current data
function deselect_all_current_data()
{
	var index_of_eid;
	var local_eid;

    for (var i=0; i < current_data.length; i++)
    {
		local_eid 		= current_data[i];
		index_of_eid 	= saved_data.indexOf(local_eid);

		//--------------------------------controls saved_data and current data
		if(index_of_eid != -1)
		{
			//remove this eid from the saved_data array
			saved_data.splice(index_of_eid, 1);
			//$(selected_row).remove();

			//remove the row from the saved data table
			$("#saved_data").find("[data-id='"+ local_eid +"']").remove();

			//find checkbox in current data remove checkmark
			$("#current_data").find("[data-id='"+local_eid+"']").find('.current_data_selector').prop('checked', false);
		}
		//--------------------------------controls saved_data and current data

	}

	show_hide_saved_data_table();

}
//--------------------------------deselect all current data

//----------------------------------------remove all current data
function remove_all_current_data()
{
    //this destroys the current_data table
    $("#current_data").find("table").remove();

    $("#select_all_current_data").css("display","none");
    $("#clear_current_data").css("display","none");
    $("#current_data_number").hide();
	$("#current_data").hide();
}
//----------------------------------------remove all current data

//----------------------actions when user clicks on the saved data selector
  $( '#saved_data' ).on( "click", '.saved_data_selector', function( event )
    {

            var selected_row    = $(this).parent().parent();
            var current_id      = $(selected_row).data('id');
            var index           = saved_data.indexOf(current_id);

            //--------------------------------controls saved_data
            if(index != -1)
            {
                //remove eid from array
                saved_data.splice(index, 1);
                $(selected_row).remove();

                //find checkbox in current data remove checkmark
                $("#current_data").find("[data-id='"+current_id+"']").find('.current_data_selector').prop('checked', false);
            }
            //--------------------------------controls saved_data

            show_hide_saved_data_table();

    });
//----------------------actions when user clicks on the saved data selector

//------------------------clear current data
$( "#clear_current_data" ).click(function() {
	remove_all_current_data();
});
//------------------------clear current data

//---actions when user clicks on the clear saved data button
$( "#clear_saved_data" ).click(function() {

    for (var i=0; i < saved_data.length; i++)
    {
        //removes each row from saved data table
        $("#saved_data").find("[data-id='"+ saved_data[i] +"']").remove();

        //unchecks the corresponding element in the current data table
        $("[data-id='"+ saved_data[i] +"']").find(".current_data_selector").prop('checked', false);

    }

    //this resets the saved data array
    saved_data = [];

    show_hide_saved_data_table();

});
//---actions when user clicks on the clear saved data button

//-----------------------------------------actions when user clicks download data button
$( "#download_data" ).click(function() {
    if( saved_data.length > 0)
    {
        var database_types  = [];
        var check_boxes     = $(".db_type_filter");

        $(check_boxes).each(function(index, value) {

            //get the value if check box is selected
            if($(check_boxes[index] ).prop('checked'))
            {
                database_types.push($(check_boxes[index]).val());
            }

        });

        retrieve_database(database_types, saved_data);
    }

});
//-----------------------------------------actions when user clicks download data button

//--------------------controls visibility of download button
function enable_disable_download_button()
{

   var is_any_check_box_checked = false;
   $(".db_type_filter").each( function( index, value )
    {
        //if any check box is checked then enabled download button and exit function
        if( $(value).prop("checked") )
        {
            $("#download_data").prop('disabled', false);
            is_any_check_box_checked = true;

            //return false is to break the each loop
            return false;
        }
    });

    if(is_any_check_box_checked == false)
    {
        //if none of the check boxes were checked, then disable download button
        $("#download_data").prop('disabled', true);
    }

}
//--------------------controls visibility of download button

//--------------actions when user clicks on check box

$(".db_type_filter").click(function(){
    enable_disable_download_button();
});

//--------------actions when user clicks on check box

//--------------------------------------------------raising up the download modal
$("#raise_up_download_modal").click(function(){

   $(".db_type_filter").each( function( index, value )
    {
        //uncheck all the values
        $(value).prop("checked", false);
        $("#download_data").prop('disabled', true);
    });

});
//--------------------------------------------------raising up the download modal

//---------------------------------------------remove vertical scroll bar for modal
$('.modal').on('show.bs.modal', function() {

	$("body").css("overflow-y","hidden");
	$(this).css("overflow-y","hidden");
});

$('.modal').on('hide.bs.modal', function() {

	$("body").css("overflow-y","auto");
	$(this).css("overflow-y","auto");
});
//---------------------------------------------remove vertical scroll bar for modal

<commit_msg>when user removes all saved data, now the select-deselect all checkbox is deselected<commit_after>
//----------------------obtains data by right clicking on cluster or point
$( '#map' ).on( "click", '.obtain_data_from_cluster_or_marker', function( event )
{
    var geohashes = $(this).data("geohashes");
    var eid_count = $(this).data("eid_count");
    map.closePopup();

    retrieve_data('none', geohashes, eid_count);

});
//----------------------obtains data by right clicking on cluster or point

//-----------------------------------------------apply filter
$( "#apply_filter" ).click(function()
{
	map.closePopup();
    var crop_id   = $("#crop_filter").val();
    obtain_specific_crop_map_population(crop_id);

});
//-----------------------------------------------apply filter

//----------------------------------generate saved_data row from a current data row
function generate_saved_data_row_from_current_data_row(selected_row, current_id)
{
    var local_crid;
    var local_pdate;
    var local_soil_texture;
    var local_institution;
    var local_country;
    var local_exname;
    var local_agmip_rating;
    var local_eid;

    local_crid          = $(selected_row).find("[data-type='crid']").text();
    local_pdate         = $(selected_row).find("[data-type='pdate']").text();
    local_soil_texture  = $(selected_row).find("[data-type='soil']").text();
    local_institution   = $(selected_row).find("[data-type='institution']").text();
    local_country       = $(selected_row).find("[data-type='country']").text();
    local_exname        = $(selected_row).find("[data-type='exname']").text();
    local_agmip_rating  = $(selected_row).find("[data-type='rating']").text();
    local_eid           = $(selected_row).find("[data-type='eid']").text();

    var row     =   "" + "<input class='saved_data_selector' type='checkbox' >" + "	";

    row = row + ""+         local_crid  +"	";
    row = row + ""+        local_pdate +"	";
    row = row + ""+         local_soil_texture +"	";
    row = row + ""+  local_institution  +"	";
    row = row + ""+      local_country  +"	";
    row = row + ""+       local_exname +"	";
    row = row + ""+       local_agmip_rating +"	";

    row = "
"+ row +"
";

    //appends row after the last row
    $("#saved_data").find("table tr:last").after(row)
}
//----------------------------------generate saved_data row from a current data row

//----------------------actions when user clicks on the current data selector
$( '#current_data' ).on( "click", '.current_data_selector', function( event )
{
        var selected_row    = $(this).parent().parent();
        /*
            when the user clicks on the current data selector,
            this function finds the id of the selected row and does the following:
            (a) adds it to the saved data array if it is not there
            (b) removes it from the saved data array if it is there
        */
        //current id represents the eid, the id is stored as data attribute in the row
        var current_id      = selected_row.data('id');
        var index           = saved_data.indexOf(current_id);

        //--------------------------------controls saved_data
        if(index == -1)
        {
            //add eid to array if eid is not in array already
            saved_data.push( current_id );

            //-----------------appends rows to saved table
            generate_saved_data_row_from_current_data_row(selected_row, current_id);
            //-----------------appends rows to saved table
        }
        else
        {
            //remove eid from array
            saved_data.splice(index, 1);
            $("#saved_data").find("[data-id='"+current_id+"']").remove();
        }
        //--------------------------------controls saved_data

        show_hide_saved_data_table();

});
//----------------------actions when user clicks on the current data selector

//--------------------------------------------obtains data by clicking obtain data button
/*
    This function obtains data from database and builds current data table
*/
$( "#obtain_data" ).click(function() {

    var crop_id     = $("#crop_filter").val();
    var geohashes   = [];
    var map_bounds  = map.getBounds();
    var eid_count   = 0;
    var marker;

    //---selects geohashes if marker is within the bounds of view
    for (var i=0; i < markers.length; i++)
    {
        marker = markers[i];
        if( map_bounds.contains([parseFloat(marker['_latlng']["lat"]) ,parseFloat(marker['_latlng']["lng"])]) )
        {
            geohashes.push(marker['options']['geohash']);
            eid_count = eid_count + parseFloat(marker['options']['count']);
        }
    }
    //---selects geohashes if marker is within the bounds of view

    //retrieve_data triggers the build table function
    retrieve_data(crop_id, geohashes, eid_count);

});
//--------------------------------------------obtains data by clicking obtain data button

//--------------------------------------------------------when user hits checkbox
$( '#current_data' ).on( "click", '#select_all_current_data', function()
{

	if( $(this).prop('checked')  )
	{
		select_all_current_data();
	}
	else
	{
		deselect_all_current_data();
	}

});
//--------------------------------------------------------when user hits checkbox

//----------------------------------------select all current data
function select_all_current_data()
{
    var rows = $("#current_data table").find("tr");
    var selected_row;
    var current_id;
    var index;

    var name;
    var institution;
    var row;

    $(rows).each(function(i, value)
    {

        if(i > 0) //the first row is the title for columns, so skip it
        {

            selected_row    = rows[i];
            current_id      = $(selected_row).data('id');

            index           = saved_data.indexOf(current_id);

            if(index == -1)
            {
                $(selected_row).find(".current_data_selector").prop('checked', true);

                //add eid to array if eid is not in array already
                saved_data.push( current_id );

                //-----------------appends rows to saved table
                generate_saved_data_row_from_current_data_row(selected_row, current_id);
                //-----------------appends rows to saved table
            }

            show_hide_saved_data_table();

        }

    });

}
//----------------------------------------select all current data

//--------------------------------deselect all current data
function deselect_all_current_data()
{
	var index_of_eid;
	var local_eid;

    for (var i=0; i < current_data.length; i++)
    {
		local_eid 		= current_data[i];
		index_of_eid 	= saved_data.indexOf(local_eid);

		//--------------------------------controls saved_data and current data
		if(index_of_eid != -1)
		{
			//remove this eid from the saved_data array
			saved_data.splice(index_of_eid, 1);
			//$(selected_row).remove();

			//remove the row from the saved data table
			$("#saved_data").find("[data-id='"+ local_eid +"']").remove();

			//find checkbox in current data remove checkmark
			$("#current_data").find("[data-id='"+local_eid+"']").find('.current_data_selector').prop('checked', false);
		}
		//--------------------------------controls saved_data and current data

	}

	show_hide_saved_data_table();

}
//--------------------------------deselect all current data

//----------------------------------------remove all current data
function remove_all_current_data()
{
    //this destroys the current_data table
    $("#current_data").find("table").remove();

    $("#select_all_current_data").css("display","none");
    $("#clear_current_data").css("display","none");
    $("#current_data_number").hide();
	$("#current_data").hide();
}
//----------------------------------------remove all current data

//----------------------actions when user clicks on the saved data selector
  $( '#saved_data' ).on( "click", '.saved_data_selector', function( event )
    {

            var selected_row    = $(this).parent().parent();
            var current_id      = $(selected_row).data('id');
            var index           = saved_data.indexOf(current_id);

            //--------------------------------controls saved_data
            if(index != -1)
            {
                //remove eid from array
                saved_data.splice(index, 1);
                $(selected_row).remove();

                //find checkbox in current data remove checkmark
                $("#current_data").find("[data-id='"+current_id+"']").find('.current_data_selector').prop('checked', false);
            }
            //--------------------------------controls saved_data

            show_hide_saved_data_table();

    });
//----------------------actions when user clicks on the saved data selector

//------------------------clear current data
$( "#clear_current_data" ).click(function() {
	remove_all_current_data();
});
//------------------------clear current data

//---actions when user clicks on the clear saved data button
$( "#clear_saved_data" ).click(function() {

    for (var i=0; i < saved_data.length; i++)
    {
        //removes each row from saved data table
        $("#saved_data").find("[data-id='"+ saved_data[i] +"']").remove();

        //unchecks the corresponding element in the current data table
        $("[data-id='"+ saved_data[i] +"']").find(".current_data_selector").prop('checked', false);

    }

    //this resets the saved data array
    saved_data = [];

    show_hide_saved_data_table();
	$("#select_all_current_data").prop('checked', false);
});
//---actions when user clicks on the clear saved data button

//-----------------------------------------actions when user clicks download data button
$( "#download_data" ).click(function() {
    if( saved_data.length > 0)
    {
        var database_types  = [];
        var check_boxes     = $(".db_type_filter");

        $(check_boxes).each(function(index, value) {

            //get the value if check box is selected
            if($(check_boxes[index] ).prop('checked'))
            {
                database_types.push($(check_boxes[index]).val());
            }

        });

        retrieve_database(database_types, saved_data);
    }

});
//-----------------------------------------actions when user clicks download data button

//--------------------controls visibility of download button
function enable_disable_download_button()
{

   var is_any_check_box_checked = false;
   $(".db_type_filter").each( function( index, value )
    {
        //if any check box is checked then enabled download button and exit function
        if( $(value).prop("checked") )
        {
            $("#download_data").prop('disabled', false);
            is_any_check_box_checked = true;

            //return false is to break the each loop
            return false;
        }
    });

    if(is_any_check_box_checked == false)
    {
        //if none of the check boxes were checked, then disable download button
        $("#download_data").prop('disabled', true);
    }

}
//--------------------controls visibility of download button

//--------------actions when user clicks on check box

$(".db_type_filter").click(function(){
    enable_disable_download_button();
});

//--------------actions when user clicks on check box

//--------------------------------------------------raising up the download modal
$("#raise_up_download_modal").click(function(){

   $(".db_type_filter").each( function( index, value )
    {
        //uncheck all the values
        $(value).prop("checked", false);
        $("#download_data").prop('disabled', true);
    });

});
//--------------------------------------------------raising up the download modal

//---------------------------------------------remove vertical scroll bar for modal
$('.modal').on('show.bs.modal', function() {

	$("body").css("overflow-y","hidden");
	$(this).css("overflow-y","hidden");
});

$('.modal').on('hide.bs.modal', function() {

	$("body").css("overflow-y","auto");
	$(this).css("overflow-y","auto");
});
//---------------------------------------------remove vertical scroll bar for modal

<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2014 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

CLASS({
  package: 'foam.graphics',
  name:  'CView',
  label: 'CView',

  requires: [
    'foam.graphics.PositionedCViewView',
    'foam.graphics.CViewView'
  ],

  traits: [ 'foam.patterns.ChildTreeTrait' ],

  documentation: function() {/*
      The base class for a canvas item. A $$DOC{ref:'.'} can be directly inserted
      into the DOM with $$DOC{ref:'.write'}, and will generate a $$DOC{ref:'CViewView'}
      wrapper.

$$DOC{ref:'.'} submodels directly nest inside each other, with a single
      root $$DOC{ref:'.'} attached to the canvas. Use $$DOC{ref:'.addChild'} to attach a new
      $$DOC{ref:'.'} to the scene graph:

            var rootNode = this.X.CView.create({width:300, height:200});

            rootNode.write(document); // a CViewView wrapper is created for us

            rootNode.addChild(this.X.Circle.create({x:30, y:50, radius: 30, color: 'blue'});

            rootNode.addChild(this.X.Label.create({x: 50, y: 30, text: "Hello", color: 'black'});

When modeling your own $$DOC{ref:'foam.graphics.CView'} submodel, override $$DOC{ref:'.paintSelf'}
      to render your content. Children will automatically be painted for you. For more direct
      control over child rendering, override $$DOC{ref:'.paint'}.
    */},

  properties: [
    {
      name:  'view',
      type:  'Canvas2',
      postSet: function(_, view) {
        for ( var key in this.children ) {
          var child = this.children[key];
          child.view = view;
          if ( view ) child.addListener(view.paint);
        }
      },
      transient: true,
      hidden: true,
      documentation: function() {/* The canvas view this scene draws into */ }
    },
    {
      name:  'canvas',
      getter: function() { return this.view && this.view.canvas; },
      transient: true,
      hidden: true,
      documentation: function() {/* Safe getter for the canvas view this scene draws into */ }
    },
    {
      name:  '$',
      getter: function() { return this.view && this.view.$; },
      transient: true,
      hidden: true,
      documentation: function() {/* Safe getter for the canvas DOM element this scene draws into */ }
    },
    {
      name: 'state',
      defaultValue: 'initial',
      documentation: function() {/* Indicates if canvas setup is in progress ('initial'),
                                  or ready to paint ('active'). */}
    },
    {
      name: 'suspended',
      model_: 'BooleanProperty',
      defaultValue: false,
      documentation: function() {/*
          Suspend painting. While this property is true, this
          $$DOC{ref:'foam.graphics.CView'} will not paint itself or its
          children.
        */},
    },
    {
      name: 'className',
      help: 'CSS class name(s), space separated. Used if adapted with a CViewView.',
      defaultValue: '',
      documentation: function() {/* CSS class name(s), space separated.
          Only used if this is the root node adapted with a $$DOC{ref:'CViewView'}. */}
    },
    {
      model_: 'FloatProperty',
      name:  'x',
      defaultValue: 0,
      documentation: function() {/*
          The X offset of this view relative to its parent. */}
    },
    {
      model_: 'FloatProperty',
      name:  'y',
      defaultValue: 0,
      documentation: function() {/*
          The Y offset of this view relative to its parent. */}
    },
    {
      name: 'canvasX',
      getter: function() { return this.x + ( this.parent ? this.parent.canvasX : 0 ); }
    },
    {
      name: 'canvasY',
      getter: function() { return this.y + ( this.parent ? this.parent.canvasY : 0 ); }
    },
    {
      model_: 'IntProperty',
      name:  'width',
      defaultValue: 10,
      documentation: function() {/*
          The width of this view. Painting is not automatically clipped, so a view
          may render outside of its apparent rectangle. */}
    },
    {
      model_: 'IntProperty',
      name:  'height',
      defaultValue: 10,
      documentation: function() {/*
          The height of this view. Painting is not automatically clipped, so a view
          may render outside of its apparent rectangle. */}
    },
    {
      model_: 'FloatProperty',
      name:  'alpha',
      defaultValue: 1,
      documentation: function() {/*
          The desired opacity of the content, from 0:transparent to 1:opaque.
          Child views do not inherit and are not limited by this value. */}
    },
    {
      name:  'color',
      label: 'Foreground Color',
      type:  'String',
      defaultValue: 'black',
      documentation: function() {/*
          The foreground color for rendering primary content. */}
    },
    {
      name:  'background',
      label: 'Background Color',
      type:  'String',
      defaultValue: 'white',
      documentation: function() {/*
          The optional background color for opaque items that $$DOC{ref:'.erase'}
          their background. */}
    },
    {
      name: 'font',
      documentation: function() {/*
          The font to use for rendering text, in CSS string format: '24px Roboto'. */}
    },
    {
      name: 'clipped',
      model_: 'BooleanProperty',
      defaultValue: false
    }
  ],

  methods: {
    toView_: function() { /* Internal. Creates a CViewView wrapper. */
      if ( ! this.view ) {
        var params = {cview: this};
        if ( this.className )   params.className   = this.className;
        if ( this.tooltip )     params.tooltip     = this.tooltip;
        if ( this.speechLabel ) params.speechLabel = this.speechLabel;
        if ( this.tabIndex )    params.tabIndex    = this.tabIndex;
        if ( this.role )        params.role        = this.role;
        if ( this.data$ )       params.data$       = this.data$;
        this.view = this.CViewView.create(params);
      }
      return this.view;
    },

    toPositionedView_: function() { /* Internal. Creates a PositionedCViewView wrapper. */
      if ( ! this.view ) {
        var params = {cview: this};
        if ( this.className ) params.className = this.className;
        this.view = this.PositionedCViewView.create(params);
      }
      return this.view;
    },

    initCView: function() { /* Override in submodels for initialization. Callled
          once on first $$DOC{ref:'.paint'} when transitioning from 'initial'
          to 'active' '$$DOC{ref:'.state'}. */ },

    write: function(document) { /* Inserts this $$DOC{ref:'foam.graphics.CView'} into the DOM
                                   with an $$DOC{ref:'foam.graphics.AbstractCViewView'} wrapper. */
      var v = this.toView_();
      document.writeln(v.toHTML());
      v.initHTML();
    },

    addChild: function(child) { /* Adds a child $$DOC{ref:'foam.graphics.CView'} to the scene
                                   under this. */
      this.SUPER(child);

      if ( this.view ) {
        child.view = this.view;
        child.addListener(this.view.paint);
      }
      return this;
    },

    removeChild: function(child) { /* Removes a child from the scene. */
      this.SUPER(child);
      child.view = undefined;
      child.removeListener(this.view.paint);
      return this;
    },

    erase: function() { /* Wipes the canvas area of this $$DOC{ref:'.'}. Primarily used
                          by the root node to clear the entire canvas, but an opaque child
                          may choose to erase its own area, if required. */
      this.canvas.clearRect(0, 0, this.width, this.height);
      this.canvas.fillStyle = this.background;
      this.canvas.fillRect(0, 0, this.width, this.height);
    },

    paintChildren: function() { /* Paints each child. */
      for ( var i = 0 ; i < this.children.length ; i++ ) {
        var child = this.children[i];
        this.canvas.save();
        this.canvas.beginPath(); // reset any existing path (canvas.restore() does not affect path)
        child.paint();
        this.canvas.restore();
      }
    },

    paintSelf: function() { /* Implement this in sub-models to do your painting. */ },

    paint: function() { /* Translates the canvas to our ($$DOC{ref:'.x'}, $$DOC{ref:'.y'}),
                          does a $$DOC{ref:'.paintSelf'} then paints all the children. */
      if ( ! this.$ ) return; // no canvas element, so do nothing
      if ( this.state === 'initial' ) {
        this.initCView();
        this.state = 'active';
      }
      if ( this.suspended ) return; // we allowed initialization, but if suspended don't paint

      this.canvas.save();
      this.canvas.translate(this.x, this.y);
      if ( this.clipped ) {
        this.canvas.rect(0,0,this.width,this.height);
        this.canvas.clip();
      }
      this.paintSelf();
      this.paintChildren();
      this.canvas.restore();
    },

    mapToParent: function(point) { /* Maps a coordinate from this to our parents'. */
      point.x += this.x;
      point.y += this.y;
      return point;
    },

    mapToCanvas: function(point) { /* Maps a coordinate from this to the canvas.
                    Useful for sharing a point between sibling or cousin items. */
      this.mapToParent(point);
      if ( this.parent && this.parent.mapToCanvas )
        this.parent.mapToCanvas(point);
      return point;
    },
  }
});
<commit_msg>Fix for CView.write(document) when called async.<commit_after>/**
 * @license
 * Copyright 2014 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

CLASS({
  package: 'foam.graphics',
  name:  'CView',
  label: 'CView',

  requires: [
    'foam.graphics.PositionedCViewView',
    'foam.graphics.CViewView'
  ],

  traits: [ 'foam.patterns.ChildTreeTrait' ],

  documentation: function() {/*
      The base class for a canvas item. A $$DOC{ref:'.'} can be directly inserted
      into the DOM with $$DOC{ref:'.write'}, and will generate a $$DOC{ref:'CViewView'}
      wrapper.

$$DOC{ref:'.'} submodels directly nest inside each other, with a single
      root $$DOC{ref:'.'} attached to the canvas. Use $$DOC{ref:'.addChild'} to attach a new
      $$DOC{ref:'.'} to the scene graph:

            var rootNode = this.X.CView.create({width:300, height:200});

            rootNode.write(document); // a CViewView wrapper is created for us

            rootNode.addChild(this.X.Circle.create({x:30, y:50, radius: 30, color: 'blue'});

            rootNode.addChild(this.X.Label.create({x: 50, y: 30, text: "Hello", color: 'black'});

When modeling your own $$DOC{ref:'foam.graphics.CView'} submodel, override $$DOC{ref:'.paintSelf'}
      to render your content. Children will automatically be painted for you. For more direct
      control over child rendering, override $$DOC{ref:'.paint'}.
    */},

  properties: [
    {
      name:  'view',
      type:  'Canvas2',
      postSet: function(_, view) {
        for ( var key in this.children ) {
          var child = this.children[key];
          child.view = view;
          if ( view ) child.addListener(view.paint);
        }
      },
      transient: true,
      hidden: true,
      documentation: function() {/* The canvas view this scene draws into */ }
    },
    {
      name:  'canvas',
      getter: function() { return this.view && this.view.canvas; },
      transient: true,
      hidden: true,
      documentation: function() {/* Safe getter for the canvas view this scene draws into */ }
    },
    {
      name:  '$',
      getter: function() { return this.view && this.view.$; },
      transient: true,
      hidden: true,
      documentation: function() {/* Safe getter for the canvas DOM element this scene draws into */ }
    },
    {
      name: 'state',
      defaultValue: 'initial',
      documentation: function() {/* Indicates if canvas setup is in progress ('initial'),
                                  or ready to paint ('active'). */}
    },
    {
      name: 'suspended',
      model_: 'BooleanProperty',
      defaultValue: false,
      documentation: function() {/*
          Suspend painting. While this property is true, this
          $$DOC{ref:'foam.graphics.CView'} will not paint itself or its
          children.
        */},
    },
    {
      name: 'className',
      help: 'CSS class name(s), space separated. Used if adapted with a CViewView.',
      defaultValue: '',
      documentation: function() {/* CSS class name(s), space separated.
          Only used if this is the root node adapted with a $$DOC{ref:'CViewView'}. */}
    },
    {
      model_: 'FloatProperty',
      name:  'x',
      defaultValue: 0,
      documentation: function() {/*
          The X offset of this view relative to its parent. */}
    },
    {
      model_: 'FloatProperty',
      name:  'y',
      defaultValue: 0,
      documentation: function() {/*
          The Y offset of this view relative to its parent. */}
    },
    {
      name: 'canvasX',
      getter: function() { return this.x + ( this.parent ? this.parent.canvasX : 0 ); }
    },
    {
      name: 'canvasY',
      getter: function() { return this.y + ( this.parent ? this.parent.canvasY : 0 ); }
    },
    {
      model_: 'IntProperty',
      name:  'width',
      defaultValue: 10,
      documentation: function() {/*
          The width of this view. Painting is not automatically clipped, so a view
          may render outside of its apparent rectangle. */}
    },
    {
      model_: 'IntProperty',
      name:  'height',
      defaultValue: 10,
      documentation: function() {/*
          The height of this view. Painting is not automatically clipped, so a view
          may render outside of its apparent rectangle. */}
    },
    {
      model_: 'FloatProperty',
      name:  'alpha',
      defaultValue: 1,
      documentation: function() {/*
          The desired opacity of the content, from 0:transparent to 1:opaque.
          Child views do not inherit and are not limited by this value. */}
    },
    {
      name:  'color',
      label: 'Foreground Color',
      type:  'String',
      defaultValue: 'black',
      documentation: function() {/*
          The foreground color for rendering primary content. */}
    },
    {
      name:  'background',
      label: 'Background Color',
      type:  'String',
      defaultValue: 'white',
      documentation: function() {/*
          The optional background color for opaque items that $$DOC{ref:'.erase'}
          their background. */}
    },
    {
      name: 'font',
      documentation: function() {/*
          The font to use for rendering text, in CSS string format: '24px Roboto'. */}
    },
    {
      name: 'clipped',
      model_: 'BooleanProperty',
      defaultValue: false
    }
  ],

  methods: {
    toView_: function() { /* Internal. Creates a CViewView wrapper. */
      if ( ! this.view ) {
        var params = {cview: this};
        if ( this.className )   params.className   = this.className;
        if ( this.tooltip )     params.tooltip     = this.tooltip;
        if ( this.speechLabel ) params.speechLabel = this.speechLabel;
        if ( this.tabIndex )    params.tabIndex    = this.tabIndex;
        if ( this.role )        params.role        = this.role;
        if ( this.data$ )       params.data$       = this.data$;
        this.view = this.CViewView.create(params);
      }
      return this.view;
    },

    toPositionedView_: function() { /* Internal. Creates a PositionedCViewView wrapper. */
      if ( ! this.view ) {
        var params = {cview: this};
        if ( this.className ) params.className = this.className;
        this.view = this.PositionedCViewView.create(params);
      }
      return this.view;
    },

    initCView: function() { /* Override in submodels for initialization. Callled
          once on first $$DOC{ref:'.paint'} when transitioning from 'initial'
          to 'active' '$$DOC{ref:'.state'}. */ },

    write: function(document) { /* Inserts this $$DOC{ref:'foam.graphics.CView'} into the DOM
                                   with an $$DOC{ref:'foam.graphics.AbstractCViewView'} wrapper. */
      this.toView_().write(document);
    },

    addChild: function(child) { /* Adds a child $$DOC{ref:'foam.graphics.CView'} to the scene
                                   under this. */
      this.SUPER(child);

      if ( this.view ) {
        child.view = this.view;
        child.addListener(this.view.paint);
      }
      return this;
    },

    removeChild: function(child) { /* Removes a child from the scene. */
      this.SUPER(child);
      child.view = undefined;
      child.removeListener(this.view.paint);
      return this;
    },

    erase: function() { /* Wipes the canvas area of this $$DOC{ref:'.'}. Primarily used
                          by the root node to clear the entire canvas, but an opaque child
                          may choose to erase its own area, if required. */
      this.canvas.clearRect(0, 0, this.width, this.height);
      this.canvas.fillStyle = this.background;
      this.canvas.fillRect(0, 0, this.width, this.height);
    },

    paintChildren: function() { /* Paints each child. */
      for ( var i = 0 ; i < this.children.length ; i++ ) {
        var child = this.children[i];
        this.canvas.save();
        this.canvas.beginPath(); // reset any existing path (canvas.restore() does not affect path)
        child.paint();
        this.canvas.restore();
      }
    },

    paintSelf: function() { /* Implement this in sub-models to do your painting. */ },

    paint: function() { /* Translates the canvas to our ($$DOC{ref:'.x'}, $$DOC{ref:'.y'}),
                          does a $$DOC{ref:'.paintSelf'} then paints all the children. */
      if ( ! this.$ ) return; // no canvas element, so do nothing
      if ( this.state === 'initial' ) {
        this.initCView();
        this.state = 'active';
      }
      if ( this.suspended ) return; // we allowed initialization, but if suspended don't paint

      this.canvas.save();
      this.canvas.translate(this.x, this.y);
      if ( this.clipped ) {
        this.canvas.rect(0,0,this.width,this.height);
        this.canvas.clip();
      }
      this.paintSelf();
      this.paintChildren();
      this.canvas.restore();
    },

    mapToParent: function(point) { /* Maps a coordinate from this to our parents'. */
      point.x += this.x;
      point.y += this.y;
      return point;
    },

    mapToCanvas: function(point) { /* Maps a coordinate from this to the canvas.
                    Useful for sharing a point between sibling or cousin items. */
      this.mapToParent(point);
      if ( this.parent && this.parent.mapToCanvas )
        this.parent.mapToCanvas(point);
      return point;
    },
  }
});
<|endoftext|>