<commit_before>/**
 * The function `reverse` computes the reversal
 * of a given `String` (don't copy-pasta!)
 *
 * @param {String} S to reverse
 * @return {String}
 */
function reverse(S){
  S = String(S);
  var newstring=[];
  var newstringcounter = 0;
  var resultstring = "";
  for (count = S.length-1; count >= 0; count --) {
      newstring[newstringcounter]=S[count];
      newstringcounter ++;
  }
  for (count =0; count < S.length; count ++){
    resultstring=resultstring.concat(newstring[count]);
  }

//  console.log(resultstring); //want to see result as the function runs
  return resultstring;
}

//debuging caese
//reverse("A");
//reverse("cat");
//reverse("ward");
//reverse("books");

 console.assert(reverse("") === ""); // Really
console.assert(reverse("A") === "A"); // Jerk.
console.assert(reverse("cat") === "tac");
console.assert(reverse("ward") === "draw");
console.assert(reverse("books") === "skoob"); // Rokey, dokey...
console.assert(
  reverse("we don't want no trouble") === "elbuort on tnaw t'nod ew"
);

/**
 * Function `encode` accepts a `String` and produces
 * the appropriate ROT13 "encoded" version, i.e. every
 * character in `phrase` is "rotated" ahead by 13 characters.
 *
 * @see String.prototype.charCodeAt
 * @see String.prototype.fromCharCode
 * @see http://en.wikipedia.org/wiki/ROT13
 *
 * // Start with just `phrase`...
 * @param {String} phrase to encode
 * // Add `N` in part 2!
 * // @param {Number} N rotation to apply, default 13
 * @return {String} encoded with ROT13
 */
function encode(phrase/*, N */){
  var alphabet = {0: "a", 1: "b", 2: "c", 3: "d", 4: "e", 5: "f", 6: "g", 7: "h",
8: "i", 9: "j", 10: "k", 11: "l", 12: "m", 13: "n", 14: "o", 15: "p", 16: "q", 17: "r",
18: "s", 19: "t", 20: "u", 21: "v", 22: "w", 23: "x", 24: "y", 25: "z"};

}

/**
 * Function `decode` accepts a `phrase` and `N` and
 * decoded it appropriately, i.e. every _word_ character
 * in `phrase` is rotated backward by `N` characters.
 *
 * @param {String} phrase to decode
 * @param {Number} N rotation to apply, default 13
 * @return {String} decoded by ROT-N
 */
function decode(phrase, N){
    // YOUR CODE HERE
}

// Produce more examples, please...
console.assert(encode("hello") === "uryyb");
console.assert(encode("uryyb") === "hello");

console.assert(encode("hello", 2) === "jgnnq")
console.assert(decode("jgnnq", 2) === "hello")
<commit_msg>got the function encode working...suprisingly.<commit_after>/**
 * The function `reverse` computes the reversal
 * of a given `String` (don't copy-pasta!)
 *
 * @param {String} S to reverse
 * @return {String}
 */
function reverse(S){
  S = String(S);
  var newstring=[];
  var newstringcounter = 0;
  var resultstring = "";
  for (count = S.length-1; count >= 0; count --) {
      newstring[newstringcounter]=S[count];
      newstringcounter ++;
  }
  for (count =0; count < S.length; count ++){
    resultstring=resultstring.concat(newstring[count]);
  }

//  console.log(resultstring); //want to see result as the function runs
  return resultstring;
}

//debuging caese
//reverse("A");
//reverse("cat");
//reverse("ward");
//reverse("books");

 console.assert(reverse("") === ""); // Really
console.assert(reverse("A") === "A"); // Jerk.
console.assert(reverse("cat") === "tac");
console.assert(reverse("ward") === "draw");
console.assert(reverse("books") === "skoob"); // Rokey, dokey...
console.assert(
  reverse("we don't want no trouble") === "elbuort on tnaw t'nod ew"
);

/**
 * Function `encode` accepts a `String` and produces
 * the appropriate ROT13 "encoded" version, i.e. every
 * character in `phrase` is "rotated" ahead by 13 characters.
 *
 * @see String.prototype.charCodeAt
 * @see String.prototype.fromCharCode
 * @see http://en.wikipedia.org/wiki/ROT13
 *
 * // Start with just `phrase`...
 * @param {String} phrase to encode
 * // Add `N` in part 2!
 * // @param {Number} N rotation to apply, default 13
 * @return {String} encoded with ROT13
 */
function encode(phrase){
  phrase=String(phrase);
  var phraselength = phrase.length;
  var newphrase=[];
  var newphrasecounter = 0;
  var resultphrase = "";
//  var alphabet = {0: "a", 1: "b", 2: "c", 3: "d", 4: "e", 5: "f", 6: "g", 7: "h",
//8: "i", 9: "j", 10: "k", 11: "l", 12: "m", 13: "n", 14: "o", 15: "p", 16: "q", 17: "r",
//18: "s", 19: "t", 20: "u", 21: "v", 22: "w", 23: "x", 24: "y", 25: "z"};

var alphabet = ["a","b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m",
 "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y" ,"z"];

console.log("input phrase = "+phrase);

  for (count = 0; count < phrase.length; count ++){
    letterposition = alphabet.indexOf(phrase[count]);
    console.log(phrase[count]);
    if (letterposition > 12) {
        console.log("count = " + count);
        console.log("letter = " + phrase[count]);
        console.log("letter position = " + alphabet.indexOf(phrase[count]));
        newphrase = newphrase.slice(0,count)+alphabet[letterposition - 13]+newphrase.slice(count,phraselength-1);
        console.log(newphrase);
    }
    else {
      console.log("count = " + count);
      console.log("letter = " + phrase[count]);
      console.log("letter position = " + alphabet.indexOf(phrase[count]));
      newphrase = newphrase.slice(0,count)+alphabet[letterposition + 13]+newphrase.slice(count,phraselength-1);
      console.log(newphrase);
    }
//    console.log(newphrase);
  }
//  console.log(newphrase);
  return newphrase;

}

/**
 * Function `decode` accepts a `phrase` and `N` and
 * decoded it appropriately, i.e. every _word_ character
 * in `phrase` is rotated backward by `N` characters.
 *
 * @param {String} phrase to decode
 * @param {Number} N rotation to apply, default 13
 * @return {String} decoded by ROT-N
 */
function decode(phrase, N){
    // YOUR CODE HERE
}

// Produce more examples, please...
console.log(encode("uryyb"));
//console.log(encode("uryyb"));
//console.assert(encode("hello") === "uryyb");
//console.assert(encode("uryyb") === "hello");

//console.assert(encode("hello", 2) === "jgnnq")
//console.assert(decode("jgnnq", 2) === "hello")
<|endoftext|>


<commit_before>/* -*- mode: espresso; espresso-indent-level: 2; indent-tabs-mode: nil -*- */
/* vim: set softtabstop=2 shiftwidth=2 tabstop=2 expandtab: */
/* global
  OptionsDialog,
  login
*/

(function(CATMAID)
 {
  // The UI singleton
  var ui;
  Object.defineProperty(CATMAID, 'ui', {
    get: function() {
      // Initialize the singleton if it doesn't exist, yet
      if (!ui) {
        ui = new CATMAID.UI();
      }
      return ui;
    },
  });
})(CATMAID);

/* It's very easy to accidentally leave in a console.log if you're working with
 * Firebug, but this will break CATMAID for some browsers.  If window.console
 * isn't defined, create a noop version of console.log: */
if (!window.console) {
  window.console = {};
  window.console.log = function() {};
}

// Attach a general error handler
window.onerror = function(msg, url, lineno, colno, err)
{
  var info = 'An error occured in CATMAID and the current action can\'t be ' +
      'completed. You can try to reload the widget or tool you just used.';
  var detail = 'Error: ' + msg + ' URL: ' + url + ' Line: ' + lineno +
      ' Column: ' + colno + ' Stacktrace: ' + (err ? err.stack : 'N/A');

  // Log the error detail to the console
  console.log(detail);

  // Log the error in the backend, bypass the request queue and make a direct
  // AJAX call through jQuery.
  $.ajax({
    'url': django_url + 'log/error',
    'type': 'POST',
    'data': {
      'msg': detail,
    }
  });

  // Log the error object, if available
  if (err) {
    console.log('Error object:');
    console.log(err);
  } else {
    console.log('No error object was provided');
  }

  // Use alert() to inform the user, if the error function isn't available for
  // some reason
  if (CATMAID && CATMAID.error) {
    CATMAID.error(info, detail);
  } else {
    alert(info + ' Detail: ' + detail);
  }

  // Return true to indicate the exception is handled and doesn't need to be
  // shown to the user.
  return true;
};

/**
 * Creates a jQuery UI based error dialog. If detail is passed, it is hidden by
 * default. The dialog allows to expand it, however.
 */
CATMAID.ErrorDialog = function(text, detail) {
  this.dialog = document.createElement('div');
  this.dialog.setAttribute("id", "dialog-confirm");
  this.dialog.setAttribute("title", "An error occured");
  // Create error message tags
  var msg = document.createElement('p');
  msg.appendChild(document.createTextNode(text));
  this.dialog.appendChild(msg);
  // Create detail field, if detail available
  if (detail) {
    var detail_head = document.createElement('p');
    var detail_head_em = document.createElement('em');
    detail_head_em.appendChild(document.createTextNode('Show/hide detail'));
    detail_head.appendChild(detail_head_em);
    this.dialog.appendChild(detail_head);
    var detail_text = document.createElement('p');
    detail_text.appendChild(document.createTextNode(detail));
    this.dialog.appendChild(detail_text);
    // Hide detail by default and toggle display by click on header
    $(detail_text).hide();
    $(detail_head).click(function() {
      $(detail_text).toggle();
    });
    $(detail_head_em).css('cursor', 'pointer');
  }
};

CATMAID.ErrorDialog.prototype = {};

/**
 * Displays the error dialog.
 */
CATMAID.ErrorDialog.prototype.show = function() {
  $(this.dialog).dialog({
    width: '400px',
    height: 'auto',
    modal: true,
    buttons: {
      "OK": function() {
        $(this).dialog("close");
      }
    }
  });
};

/**
 * Creates a simple login dialog.
 */
CATMAID.LoginDialog = function(text, callback) {
  this.dialog = new OptionsDialog("Permission required");
  if (text) {
    this.dialog.appendMessage(text);
  }
  // Add short login text
  var login_text = "Please enter the credentials for a user with the " +
      "necessary credentials to continue to the requested information";
  this.dialog.appendMessage(login_text);
  // Add input fields
  var user_field = this.dialog.appendField('Username', 'username', '', true);
  var pass_field = this.dialog.appendField('Password', 'password', '', true);
  pass_field.setAttribute('type', 'password');
  // Align input fields better
  $(this.dialog.dialog).find('label').css('width', '25%');
  $(this.dialog.dialog).find('label').css('display', 'inline-block');

  // If OK is pressed, the dialog should cause a (re-)login
  this.dialog.onOK = function() {
    login($(user_field).val(), $(pass_field).val(), callback);
  };
};

CATMAID.LoginDialog.prototype = {};

/**
 * Displays the login dialog.
 */
CATMAID.LoginDialog.prototype.show = function() {
  this.dialog.show('400', 'auto', true);
};

/**
 * Creates a generic JSON response handler that complains when the response
 * status is different from 200 or a JSON error is set.
 */
CATMAID.jsonResponseHandler = function(success, error)
{
  return function(status, text, xml) {
    if (status === 200 && text) {
      var json = $.parseJSON(text);
      if (json.error) {
        new CATMAID.ErrorDialog(json.error, json.detail).show();
        if (typeof(error) == 'function') {
          error();
        }
      } else {
        if (typeof(success) == 'function') {
          success(json);
        }
      }
    } else {
      new CATMAID.ErrorDialog("An error occured",
          "The server returned an unexpected status: " + status).show();
      if (typeof(error) == 'function') {
        error();
      }
    }
  };
};

/**
 * Convenience function to show an error dialog.
 */
CATMAID.error = function(msg, detail)
{
  new CATMAID.ErrorDialog(msg, detail).show();
};

/**
 * Convenience function to show a growl message
 */
CATMAID.msg = function(title, msg)
{
  growlAlert(title, msg);
};

/**
 * Convenience function to show a growl info message.
 */
CATMAID.info = CATMAID.msg.bind(window, "Information");

/**
 * Convenience function to show a growl warning message.
 */
CATMAID.warn = CATMAID.msg.bind(window, "Warning");
<commit_msg>Make errors accumulate in a single ErrorDialog<commit_after>/* -*- mode: espresso; espresso-indent-level: 2; indent-tabs-mode: nil -*- */
/* vim: set softtabstop=2 shiftwidth=2 tabstop=2 expandtab: */
/* global
  OptionsDialog,
  login
*/

(function(CATMAID)
 {
  // The UI singleton
  var ui;
  Object.defineProperty(CATMAID, 'ui', {
    get: function() {
      // Initialize the singleton if it doesn't exist, yet
      if (!ui) {
        ui = new CATMAID.UI();
      }
      return ui;
    },
  });
})(CATMAID);

/* It's very easy to accidentally leave in a console.log if you're working with
 * Firebug, but this will break CATMAID for some browsers.  If window.console
 * isn't defined, create a noop version of console.log: */
if (!window.console) {
  window.console = {};
  window.console.log = function() {};
}

// Attach a general error handler
window.onerror = function(msg, url, lineno, colno, err)
{
  var info = 'An error occured in CATMAID and the current action can\'t be ' +
      'completed. You can try to reload the widget or tool you just used.';
  var detail = 'Error: ' + msg + ' URL: ' + url + ' Line: ' + lineno +
      ' Column: ' + colno + ' Stacktrace: ' + (err ? err.stack : 'N/A');

  // Log the error detail to the console
  console.log(detail);

  // Log the error in the backend, bypass the request queue and make a direct
  // AJAX call through jQuery.
  $.ajax({
    'url': django_url + 'log/error',
    'type': 'POST',
    'data': {
      'msg': detail,
    }
  });

  // Log the error object, if available
  if (err) {
    console.log('Error object:');
    console.log(err);
  } else {
    console.log('No error object was provided');
  }

  // Use alert() to inform the user, if the error function isn't available for
  // some reason
  if (CATMAID && CATMAID.error) {
    CATMAID.error(info, detail);
  } else {
    alert(info + ' Detail: ' + detail);
  }

  // Return true to indicate the exception is handled and doesn't need to be
  // shown to the user.
  return true;
};

/**
 * Creates a jQuery UI based error dialog. If detail is passed, it is hidden by
 * default. The dialog allows to expand it, however.
 */
CATMAID.ErrorDialog = function(text, detail) {
  this.dialog = document.getElementById("error-dialog-confirm");
  if (null === this.dialog) {
    this.dialog = document.createElement('div');
    this.dialog.setAttribute("id", "error-dialog-confirm");
    this.dialog.setAttribute("title", "An error occured");
  } else {
    var metaMsg = "Several errors have occured:";
    if (this.dialog.firstChild.textContent !== metaMsg) {
      this.dialog.insertAdjacentHTML("afterbegin", "<h3>" + metaMsg + "</h3>");
    }
  }
  // Create error message tags
  var msg = document.createElement('p');
  msg.appendChild(document.createTextNode(text));
  this.dialog.appendChild(msg);
  // Create detail field, if detail available
  if (detail) {
    var detail_head = document.createElement('p');
    var detail_head_em = document.createElement('em');
    detail_head_em.appendChild(document.createTextNode('Show/hide detail'));
    detail_head.appendChild(detail_head_em);
    this.dialog.appendChild(detail_head);
    var detail_text = document.createElement('p');
    detail_text.appendChild(document.createTextNode(detail));
    this.dialog.appendChild(detail_text);
    // Hide detail by default and toggle display by click on header
    $(detail_text).hide();
    $(detail_head).click(function() {
      $(detail_text).toggle();
    });
    $(detail_head_em).css('cursor', 'pointer');
  }
};

CATMAID.ErrorDialog.prototype = {};

/**
 * Displays the error dialog.
 */
CATMAID.ErrorDialog.prototype.show = function() {
  $(this.dialog).dialog({
    width: '400px',
    height: 'auto',
    maxHeight: 600,
    modal: true,
    buttons: {
      "OK": function() {
        $(this).dialog("destroy");
      }
    },
    close: function() {
      $( this ).dialog( "destroy" );
    }
  });
};

/**
 * Creates a simple login dialog.
 */
CATMAID.LoginDialog = function(text, callback) {
  this.dialog = new OptionsDialog("Permission required");
  if (text) {
    this.dialog.appendMessage(text);
  }
  // Add short login text
  var login_text = "Please enter the credentials for a user with the " +
      "necessary credentials to continue to the requested information";
  this.dialog.appendMessage(login_text);
  // Add input fields
  var user_field = this.dialog.appendField('Username', 'username', '', true);
  var pass_field = this.dialog.appendField('Password', 'password', '', true);
  pass_field.setAttribute('type', 'password');
  // Align input fields better
  $(this.dialog.dialog).find('label').css('width', '25%');
  $(this.dialog.dialog).find('label').css('display', 'inline-block');

  // If OK is pressed, the dialog should cause a (re-)login
  this.dialog.onOK = function() {
    login($(user_field).val(), $(pass_field).val(), callback);
  };
};

CATMAID.LoginDialog.prototype = {};

/**
 * Displays the login dialog.
 */
CATMAID.LoginDialog.prototype.show = function() {
  this.dialog.show('400', 'auto', true);
};

/**
 * Creates a generic JSON response handler that complains when the response
 * status is different from 200 or a JSON error is set.
 */
CATMAID.jsonResponseHandler = function(success, error)
{
  return function(status, text, xml) {
    if (status === 200 && text) {
      var json = $.parseJSON(text);
      if (json.error) {
        new CATMAID.ErrorDialog(json.error, json.detail).show();
        if (typeof(error) == 'function') {
          error();
        }
      } else {
        if (typeof(success) == 'function') {
          success(json);
        }
      }
    } else {
      new CATMAID.ErrorDialog("An error occured",
          "The server returned an unexpected status: " + status).show();
      if (typeof(error) == 'function') {
        error();
      }
    }
  };
};

/**
 * Convenience function to show an error dialog.
 */
CATMAID.error = function(msg, detail)
{
  new CATMAID.ErrorDialog(msg, detail).show();
};

/**
 * Convenience function to show a growl message
 */
CATMAID.msg = function(title, msg)
{
  growlAlert(title, msg);
};

/**
 * Convenience function to show a growl info message.
 */
CATMAID.info = CATMAID.msg.bind(window, "Information");

/**
 * Convenience function to show a growl warning message.
 */
CATMAID.warn = CATMAID.msg.bind(window, "Warning");
<|endoftext|>


<commit_before>const Schema = require('ballade').Schema;
import {Map} from 'immutable';
import dispatcher from '../dispatcher/dispatcher';
import constatns from '../constants/todos';
const TODOS = 'todos';

const todoSchema = new Schema({
    id: {
        $type: String,
        $default: (+new Date() + Math.floor(Math.random() * 999999)).toString(36)
    },
    complete: {
        $type: Boolean,
        $default: false
    },
    text: {
        $type: String,
        $default: "Ballade Getting Started"
    },
    total: {
        $type: Number,
        $default: 0
    }
});

const todosSchema = new Schema({
    total: {
        $type: Number,
        $default: 0
    },
    todos: [todoSchema]
});

// Filter specific index from todos by id
const getTodoId = ($todos, id) => {
    let index;

    $todos.some((item, i) => {
        if (item.get('id') === id) {
            index = i;
            return true;
        }
    });

    return index;
};

const todosStore = dispatcher.createImmutableStore(todosSchema, {
    [`${TODOS}/${constatns.CREATE}`]: (store, action) => {
        let $todos = store.get('todos');

        $todos = $todos.unshift(Map({
            id: (+new Date() + Math.floor(Math.random() * 999999)).toString(36),
            complete: false,
            text: action.text
        }));

        store.set('todos', $todos);
    },

    [`${TODOS}/${constatns.UPDATE}`]: (store, action) => {
        let $todos = store.get('todos');
        let index = getTodoId($todos, action.id);

        if (index !== undefined) {
            $todos = $todos.setIn([index, 'text'], action.text);
        }

        store.set('todos', $todos);
    },

    [`${TODOS}/${constatns.DELETE}`]: (store, action) => {
        let $todos = store.get('todos');
        let index = getTodoId($todos, action.id);

        if (index !== undefined) {
            $todos = $todos.splice(index, 1);
        }

        store.set('todos', $todos);
    },

    [`${TODOS}/${constatns.DELETE_COMPLETE}`]: (store, action) => {
        let $todos = store.get('todos');

        $todos = $todos.filter((item) => (
            !item.get('complete')
        ));

        store.set('todos', $todos);
    },

    [`${TODOS}/${constatns.TOGGLE}`]: (store, action) => {
        let $todos = store.get('todos');
        let index = getTodoId($todos, action.id);
        let complete;

        if (index !== undefined) {
            complete = $todos.getIn([index, 'complete']);
            $todos = $todos.setIn([index, 'complete'], !complete);
        }

        store.set('todos', $todos);
    },

    [`${TODOS}/${constatns.TOGGLE_ALL}`]: (store, action) => {
        let $todos = store.get('todos');
        const active = $todos.some((item) => !item.get('complete'));

        $todos = $todos.update((list) => (
            list.map((item) => (
                item.set('complete', active)
            ))
        ));

        store.set('todos', $todos);
    }
});

console.log(todosStore.get('total'));
console.log(todosStore.get('todos').getIn([0, 'total']));

export default todosStore;
<commit_msg>delete debug code<commit_after>const Schema = require('ballade').Schema;
import {Map} from 'immutable';
import dispatcher from '../dispatcher/dispatcher';
import constatns from '../constants/todos';
const TODOS = 'todos';

const todoSchema = new Schema({
    id: {
        $type: String,
        $default: (+new Date() + Math.floor(Math.random() * 999999)).toString(36)
    },
    complete: {
        $type: Boolean,
        $default: false
    },
    text: {
        $type: String,
        $default: "Ballade Getting Started"
    }
});

const todosSchema = new Schema({
    todos: [todoSchema]
});

// Filter specific index from todos by id
const getTodoId = ($todos, id) => {
    let index;

    $todos.some((item, i) => {
        if (item.get('id') === id) {
            index = i;
            return true;
        }
    });

    return index;
};

const todosStore = dispatcher.createImmutableStore(todosSchema, {
    [`${TODOS}/${constatns.CREATE}`]: (store, action) => {
        let $todos = store.get('todos');

        $todos = $todos.unshift(Map({
            id: (+new Date() + Math.floor(Math.random() * 999999)).toString(36),
            complete: false,
            text: action.text
        }));

        store.set('todos', $todos);
    },

    [`${TODOS}/${constatns.UPDATE}`]: (store, action) => {
        let $todos = store.get('todos');
        let index = getTodoId($todos, action.id);

        if (index !== undefined) {
            $todos = $todos.setIn([index, 'text'], action.text);
        }

        store.set('todos', $todos);
    },

    [`${TODOS}/${constatns.DELETE}`]: (store, action) => {
        let $todos = store.get('todos');
        let index = getTodoId($todos, action.id);

        if (index !== undefined) {
            $todos = $todos.splice(index, 1);
        }

        store.set('todos', $todos);
    },

    [`${TODOS}/${constatns.DELETE_COMPLETE}`]: (store, action) => {
        let $todos = store.get('todos');

        $todos = $todos.filter((item) => (
            !item.get('complete')
        ));

        store.set('todos', $todos);
    },

    [`${TODOS}/${constatns.TOGGLE}`]: (store, action) => {
        let $todos = store.get('todos');
        let index = getTodoId($todos, action.id);
        let complete;

        if (index !== undefined) {
            complete = $todos.getIn([index, 'complete']);
            $todos = $todos.setIn([index, 'complete'], !complete);
        }

        store.set('todos', $todos);
    },

    [`${TODOS}/${constatns.TOGGLE_ALL}`]: (store, action) => {
        let $todos = store.get('todos');
        const active = $todos.some((item) => !item.get('complete'));

        $todos = $todos.update((list) => (
            list.map((item) => (
                item.set('complete', active)
            ))
        ));

        store.set('todos', $todos);
    }
});

export default todosStore;
<|endoftext|>


<commit_before>/**
 * Copyright 2020 The AMP HTML Authors. 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.
 */

import {
  Action,
  StateProperty,
} from '../../amp-story/1.0/amp-story-store-service';
import {CSS} from '../../../build/amp-story-education-0.1.css';
import {Layout} from '../../../src/layout';
import {LocalizedStringId} from '../../../src/localized-strings';
import {Services} from '../../../src/services';
import {createShadowRootWithStyle} from '../../amp-story/1.0/utils';
import {dev} from '../../../src/log';
import {dict} from '../../../src/utils/object';
import {htmlFor} from '../../../src/static-template';
import {removeChildren} from '../../../src/dom';
import {toggle} from '../../../src/style';

/** @type {string} */
const TAG = 'amp-story-education';

/**
 * Generates the navigation education template.
 * @param {!Element} element
 * @return {!Element}
 */
const buildNavigationEl = element => {
  const html = htmlFor(element);
  return html`

      <button class="i-amphtml-story-education-navigation-button"></button>

  `;
};

/** @enum {string} */
const Screen = {
  ONBOARDING_NAVIGATION_TAP_AND_SWIPE: 'ontas',
};

/** @enum */
export const State = {
  HIDDEN: 0,
  NAVIGATION_TAP: 1,
  NAVIGATION_SWIPE: 2,
};

export class AmpStoryEducation extends AMP.BaseElement {
  /** @param {!AmpElement} element */
  constructor(element) {
    super(element);

    /** @private {!Element} */
    this.containerEl_ = this.win.document.createElement('div');

    /** @private @const {!../../../src/service/localization.LocalizationService} */
    this.localizationService_ = Services.localizationService(this.win);

    /** @private {?boolean} */
    this.storyPausedStateToRestore_ = null;

    /** @private {!State} */
    this.state_ = State.HIDDEN;

    /** @private @const {!../../amp-story/1.0/amp-story-store-service.AmpStoryStoreService} */
    this.storeService_ = /** @type {!../../amp-story/1.0/amp-story-store-service.AmpStoryStoreService} */ (Services.storyStoreService(
      this.win
    ));

    /** @private {?../../../src/service/viewer-interface.ViewerInterface} */
    this.viewer_ = null;
  }

  /** @override */
  buildCallback() {
    this.containerEl_.classList.add('i-amphtml-story-education');
    toggle(this.containerEl_, false);
    this.startListening_();
    createShadowRootWithStyle(this.element, this.containerEl_, CSS);

    this.viewer_ = Services.viewerForDoc(this.element);
    if (this.viewer_.isEmbedded()) {
      this.maybeShowScreen_(
        Screen.ONBOARDING_NAVIGATION_TAP_AND_SWIPE,
        State.NAVIGATION_TAP
      );
    }
  }

  /** @override */
  isLayoutSupported(layout) {
    return layout === Layout.CONTAINER;
  }

  /**
   * @private
   */
  startListening_() {
    this.containerEl_.addEventListener(
      'click',
      () => this.onClick_(),
      true /** useCapture */
    );

    // Prevent touchevents from being forwarded through viewer messaging.
    this.containerEl_.addEventListener(
      'touchstart',
      event => event.stopPropagation(),
      true /** useCapture */
    );
    this.containerEl_.addEventListener(
      'touchmove',
      event => event.stopPropagation(),
      true /** useCapture */
    );
    this.containerEl_.addEventListener(
      'touchend',
      event => event.stopPropagation(),
      true /** useCapture */
    );

    this.storeService_.subscribe(
      StateProperty.RTL_STATE,
      rtlState => this.onRtlStateUpdate_(rtlState),
      true /** callToInitialize */
    );
  }

  /**
   * Handles click events.
   * @private
   */
  onClick_() {
    if (this.state_ === State.NAVIGATION_TAP) {
      this.setState_(State.NAVIGATION_SWIPE);
      return;
    }

    this.setState_(State.HIDDEN);
  }

  /**
   * @param {boolean} rtlState
   * @private
   */
  onRtlStateUpdate_(rtlState) {
    this.mutateElement(() => {
      rtlState
        ? this.containerEl_.setAttribute('dir', 'rtl')
        : this.containerEl_.removeAttribute('dir');
    });
  }

  /**
   * @param {!State} state
   * @private
   */
  setState_(state) {
    if (this.state_ === state) {
      return;
    }

    this.state_ = state;
    let el;

    switch (state) {
      case State.HIDDEN:
        this.storeService_.dispatch(Action.TOGGLE_EDUCATION, false);
        this.mutateElement(() => {
          removeChildren(this.containerEl_);
          toggle(this.containerEl_, false);
          this.storeService_.dispatch(
            Action.TOGGLE_PAUSED,
            this.storyPausedStateToRestore_
          );
        });
        break;
      case State.NAVIGATION_TAP:
        el = buildNavigationEl(this.element);
        el.setAttribute('step', 'tap');
        el.querySelector(
          '.i-amphtml-story-education-navigation-progress'
        ).textContent = this.localizationService_.getLocalizedString(
          LocalizedStringId.AMP_STORY_EDUCATION_NAVIGATION_TAP_PROGRESS
        );
        el.querySelector(
          '.i-amphtml-story-education-navigation-instructions'
        ).textContent = this.localizationService_.getLocalizedString(
          LocalizedStringId.AMP_STORY_EDUCATION_NAVIGATION_TAP_INSTRUCTIONS
        );
        el.querySelector(
          '.i-amphtml-story-education-navigation-button'
        ).textContent = this.localizationService_.getLocalizedString(
          LocalizedStringId.AMP_STORY_EDUCATION_NAVIGATION_TAP_DISMISS
        );
        this.showTemplate_(el);
        break;
      case State.NAVIGATION_SWIPE:
        el = buildNavigationEl(this.element);
        el.setAttribute('step', 'swipe');
        el.querySelector(
          '.i-amphtml-story-education-navigation-progress'
        ).textContent = this.localizationService_.getLocalizedString(
          LocalizedStringId.AMP_STORY_EDUCATION_NAVIGATION_SWIPE_PROGRESS
        );
        el.querySelector(
          '.i-amphtml-story-education-navigation-instructions'
        ).textContent = this.localizationService_.getLocalizedString(
          LocalizedStringId.AMP_STORY_EDUCATION_NAVIGATION_SWIPE_INSTRUCTIONS
        );
        el.querySelector(
          '.i-amphtml-story-education-navigation-button'
        ).textContent = this.localizationService_.getLocalizedString(
          LocalizedStringId.AMP_STORY_EDUCATION_NAVIGATION_SWIPE_DISMISS
        );
        this.showTemplate_(el);
        break;
      default:
        dev().error(TAG, 'Unknown state %s.', state);
        break;
    }
  }

  /**
   * @param {!Element} template
   * @private
   */
  showTemplate_(template) {
    if (this.storyPausedStateToRestore_ === null) {
      this.storyPausedStateToRestore_ = !!this.storeService_.get(
        StateProperty.PAUSED_STATE
      );
    }

    this.storeService_.dispatch(Action.TOGGLE_PAUSED, true);
    this.storeService_.dispatch(Action.TOGGLE_EDUCATION, true);

    this.mutateElement(() => {
      removeChildren(this.containerEl_);
      toggle(this.containerEl_, true);
      this.containerEl_.appendChild(template);
    });
  }

  /**
   * Asks the viewer whether the navigation screen should be shown.
   * Viewer responses should be treated right away, and not cached.
   * @param {!Screen} screen Screen to show
   * @param {!State} state State to set
   * @private
   */
  maybeShowScreen_(screen, state) {
    // Only show education screens when the ampdoc is visible.
    this.getAmpDoc()
      .whenFirstVisible()
      .then(() => {
        // TODO(gmajoulet): update this method to support showing multiple
        // screens, if/when needed.
        this.viewer_
          .sendMessageAwaitResponse(
            'canShowScreens',
            dict({'screens': [{'screen': screen}]})
          )
          .then(response => {
            const shouldShow = !!(
              response &&
              response['screens'] &&
              response['screens'][0] &&
              response['screens'][0]['show']
            );
            if (shouldShow) {
              this.setState_(state);
            }
          });
      });
  }
}

AMP.extension('amp-story-education', '0.1', AMP => {
  AMP.registerElement('amp-story-education', AmpStoryEducation, CSS);
});
<commit_msg>Restrict amp-story-education to the mobile UI. (#27369)<commit_after>/**
 * Copyright 2020 The AMP HTML Authors. 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.
 */

import {
  Action,
  StateProperty,
  UIType,
} from '../../amp-story/1.0/amp-story-store-service';
import {CSS} from '../../../build/amp-story-education-0.1.css';
import {Layout} from '../../../src/layout';
import {LocalizedStringId} from '../../../src/localized-strings';
import {Services} from '../../../src/services';
import {createShadowRootWithStyle} from '../../amp-story/1.0/utils';
import {dev} from '../../../src/log';
import {dict} from '../../../src/utils/object';
import {htmlFor} from '../../../src/static-template';
import {removeChildren} from '../../../src/dom';
import {toggle} from '../../../src/style';

/** @type {string} */
const TAG = 'amp-story-education';

/**
 * Generates the navigation education template.
 * @param {!Element} element
 * @return {!Element}
 */
const buildNavigationEl = element => {
  const html = htmlFor(element);
  return html`

      <button class="i-amphtml-story-education-navigation-button"></button>

  `;
};

/** @enum {string} */
const Screen = {
  ONBOARDING_NAVIGATION_TAP_AND_SWIPE: 'ontas',
};

/** @enum */
export const State = {
  HIDDEN: 0,
  NAVIGATION_TAP: 1,
  NAVIGATION_SWIPE: 2,
};

export class AmpStoryEducation extends AMP.BaseElement {
  /** @param {!AmpElement} element */
  constructor(element) {
    super(element);

    /** @private {!Element} */
    this.containerEl_ = this.win.document.createElement('div');

    /** @private @const {!../../../src/service/localization.LocalizationService} */
    this.localizationService_ = Services.localizationService(this.win);

    /** @private {?boolean} */
    this.storyPausedStateToRestore_ = null;

    /** @private {!State} */
    this.state_ = State.HIDDEN;

    /** @private @const {!../../amp-story/1.0/amp-story-store-service.AmpStoryStoreService} */
    this.storeService_ = /** @type {!../../amp-story/1.0/amp-story-store-service.AmpStoryStoreService} */ (Services.storyStoreService(
      this.win
    ));

    /** @private {?../../../src/service/viewer-interface.ViewerInterface} */
    this.viewer_ = null;
  }

  /** @override */
  buildCallback() {
    this.containerEl_.classList.add('i-amphtml-story-education');
    toggle(this.containerEl_, false);
    this.startListening_();
    createShadowRootWithStyle(this.element, this.containerEl_, CSS);

    this.viewer_ = Services.viewerForDoc(this.element);
    const isMobileUI =
      this.storeService_.get(StateProperty.UI_STATE) === UIType.MOBILE;
    if (this.viewer_.isEmbedded() && isMobileUI) {
      this.maybeShowScreen_(
        Screen.ONBOARDING_NAVIGATION_TAP_AND_SWIPE,
        State.NAVIGATION_TAP
      );
    }
  }

  /** @override */
  isLayoutSupported(layout) {
    return layout === Layout.CONTAINER;
  }

  /**
   * @private
   */
  startListening_() {
    this.containerEl_.addEventListener(
      'click',
      () => this.onClick_(),
      true /** useCapture */
    );

    // Prevent touchevents from being forwarded through viewer messaging.
    this.containerEl_.addEventListener(
      'touchstart',
      event => event.stopPropagation(),
      true /** useCapture */
    );
    this.containerEl_.addEventListener(
      'touchmove',
      event => event.stopPropagation(),
      true /** useCapture */
    );
    this.containerEl_.addEventListener(
      'touchend',
      event => event.stopPropagation(),
      true /** useCapture */
    );

    this.storeService_.subscribe(
      StateProperty.RTL_STATE,
      rtlState => this.onRtlStateUpdate_(rtlState),
      true /** callToInitialize */
    );
  }

  /**
   * Handles click events.
   * @private
   */
  onClick_() {
    if (this.state_ === State.NAVIGATION_TAP) {
      this.setState_(State.NAVIGATION_SWIPE);
      return;
    }

    this.setState_(State.HIDDEN);
  }

  /**
   * @param {boolean} rtlState
   * @private
   */
  onRtlStateUpdate_(rtlState) {
    this.mutateElement(() => {
      rtlState
        ? this.containerEl_.setAttribute('dir', 'rtl')
        : this.containerEl_.removeAttribute('dir');
    });
  }

  /**
   * @param {!State} state
   * @private
   */
  setState_(state) {
    if (this.state_ === state) {
      return;
    }

    this.state_ = state;
    let el;

    switch (state) {
      case State.HIDDEN:
        this.storeService_.dispatch(Action.TOGGLE_EDUCATION, false);
        this.mutateElement(() => {
          removeChildren(this.containerEl_);
          toggle(this.containerEl_, false);
          this.storeService_.dispatch(
            Action.TOGGLE_PAUSED,
            this.storyPausedStateToRestore_
          );
        });
        break;
      case State.NAVIGATION_TAP:
        el = buildNavigationEl(this.element);
        el.setAttribute('step', 'tap');
        el.querySelector(
          '.i-amphtml-story-education-navigation-progress'
        ).textContent = this.localizationService_.getLocalizedString(
          LocalizedStringId.AMP_STORY_EDUCATION_NAVIGATION_TAP_PROGRESS
        );
        el.querySelector(
          '.i-amphtml-story-education-navigation-instructions'
        ).textContent = this.localizationService_.getLocalizedString(
          LocalizedStringId.AMP_STORY_EDUCATION_NAVIGATION_TAP_INSTRUCTIONS
        );
        el.querySelector(
          '.i-amphtml-story-education-navigation-button'
        ).textContent = this.localizationService_.getLocalizedString(
          LocalizedStringId.AMP_STORY_EDUCATION_NAVIGATION_TAP_DISMISS
        );
        this.showTemplate_(el);
        break;
      case State.NAVIGATION_SWIPE:
        el = buildNavigationEl(this.element);
        el.setAttribute('step', 'swipe');
        el.querySelector(
          '.i-amphtml-story-education-navigation-progress'
        ).textContent = this.localizationService_.getLocalizedString(
          LocalizedStringId.AMP_STORY_EDUCATION_NAVIGATION_SWIPE_PROGRESS
        );
        el.querySelector(
          '.i-amphtml-story-education-navigation-instructions'
        ).textContent = this.localizationService_.getLocalizedString(
          LocalizedStringId.AMP_STORY_EDUCATION_NAVIGATION_SWIPE_INSTRUCTIONS
        );
        el.querySelector(
          '.i-amphtml-story-education-navigation-button'
        ).textContent = this.localizationService_.getLocalizedString(
          LocalizedStringId.AMP_STORY_EDUCATION_NAVIGATION_SWIPE_DISMISS
        );
        this.showTemplate_(el);
        break;
      default:
        dev().error(TAG, 'Unknown state %s.', state);
        break;
    }
  }

  /**
   * @param {!Element} template
   * @private
   */
  showTemplate_(template) {
    if (this.storyPausedStateToRestore_ === null) {
      this.storyPausedStateToRestore_ = !!this.storeService_.get(
        StateProperty.PAUSED_STATE
      );
    }

    this.storeService_.dispatch(Action.TOGGLE_PAUSED, true);
    this.storeService_.dispatch(Action.TOGGLE_EDUCATION, true);

    this.mutateElement(() => {
      removeChildren(this.containerEl_);
      toggle(this.containerEl_, true);
      this.containerEl_.appendChild(template);
    });
  }

  /**
   * Asks the viewer whether the navigation screen should be shown.
   * Viewer responses should be treated right away, and not cached.
   * @param {!Screen} screen Screen to show
   * @param {!State} state State to set
   * @private
   */
  maybeShowScreen_(screen, state) {
    // Only show education screens when the ampdoc is visible.
    this.getAmpDoc()
      .whenFirstVisible()
      .then(() => {
        // TODO(gmajoulet): update this method to support showing multiple
        // screens, if/when needed.
        this.viewer_
          .sendMessageAwaitResponse(
            'canShowScreens',
            dict({'screens': [{'screen': screen}]})
          )
          .then(response => {
            const shouldShow = !!(
              response &&
              response['screens'] &&
              response['screens'][0] &&
              response['screens'][0]['show']
            );
            if (shouldShow) {
              this.setState_(state);
            }
          });
      });
  }
}

AMP.extension('amp-story-education', '0.1', AMP => {
  AMP.registerElement('amp-story-education', AmpStoryEducation, CSS);
});
<|endoftext|>


<commit_before>var isMobile = false;
var snake = [[1, 0], [0, 0]]; // An array of [x, y] pairs. The head is at [0], tail is at [-1].
var board; // top left is 0, 0, bottom right is height-1, width-1
var food = [-10, -10];  // if there is no food, use this to avoid null pointers
var refreshRate = 250; // How quickly the snake moves. (ms)
var intervalId;
var gameIsStopped = true;
// high scores (both time- and food-based) for each level
var timeHighScores = [];
var pelletHighScores = [];
var currentTimeScore = 0;

// Clears board & stops AI
function stopGame() {
  if ((snake.length - snake.level) > pelletHighScores[level]) {
    pelletHighScores[level] = snake.length - snake.level;
  }
  console.log("Game stopped");
  gameIsStopped = true;
  upLevel();
  board = [];
  board.length = width; // board[x][y]
  for (var i = 0; i < width; i++) {
    board[i] = [];
    board[i].length = height;
  }
  food = [-10, -10];
  snake = [];
  for (var i=0; i<level+3; i++) {
    snake.unshift([i, 0]);
  }
  dir = 'right';
  debugSnake();
  updateBoard();
}

// Snake collided with self, start next AI level
function startGame() {
  if (document.cookie === null) {
    document.cookie = 'speed='+refreshRate+'; highest='+maxLevel;
  } else {
    cookies = document.cookie.split(';');
    for (var c=0; c<cookies.length; c++) {
      var cookie = cookies[c].split('=');
      if (cookie[0] == 'speed') {
        changeSpeed(cookie[1]);
      } else if (cookie[0] == 'highest') {
        maxLevel = cookie[1];
      }
    }
  }
  if (!gameIsStopped) {
    return;
  }
  gameIsStopped = false;
  console.log("Game started");
  console.log(level);
  console.log(refreshRate);
  console.log("Round ended! Advancing to AI level", level);
  intervalId = window.setInterval(ai, refreshRate, level); // calls ai(level) at refreshRate
  ai(level);
}

function changeSpeed(rate) {
  refreshRate = rate; // set to new value
  window.clearInterval(intervalId);
  if (!gameIsStopped) { // ensures only one interval going on at once
    intervalId = window.setInterval(ai, refreshRate, level); // calls ai(level) at refreshRate
    ai(level);
  }
}

function debugSnake() {
  for (var s=0; s<snake.length; s++) {
    console.log(snake[s][0], snake[s][1]);
  }
}

function inSnakeTwice(string) {
  var count = 0;
  for (var s=0; s<snake.length; s++) {
    if (string == snake[s][0]+'_'+snake[s][1]) {
      count++;
    }
  }
  return count==2;
}

// String is of form x_y to allow compatibility with Board.js
function inSnake(string) {
  for (var s=0; s<snake.length; s++) {
    if (string == snake[s][0]+'_'+snake[s][1]) {
      return true;
    }
  }
  return false;
}

function moveHead(dir) {
  // move snake's head in given direction
  // 'right', 'left', 'up', and 'down'
  var newHead = snake[0].slice(0); // Clone so we don't move the head by accident.
  if (dir == 'right') {
    if (newHead[0] == width - 1) {
      throw 'cannot move right from ' + newHead[0] + ', ' + newHead[1];
    } else if (inSnake((newHead[0]+1)+'_'+newHead[1])) {
      window.clearInterval(intervalId);
      window.setTimeout(stopGame, 10*refreshRate
);
    }
    newHead[0]++;
  } else if (dir == 'left') {
    if (newHead[0] === 0) {
      throw 'cannot move left from ' + newHead[0] + ', ' + newHead[1];
    } else if (inSnake((newHead[0]-1)+'_'+newHead[1])) {
      window.clearInterval(intervalId);
      window.setTimeout(stopGame, 10*refreshRate
);
    }
    newHead[0]--;
  } else if (dir == 'up') {  // add new head above current one
    if (newHead[1] === 0) {
      throw 'cannot move up from ' + newHead[0] + ', ' + newHead[1];
    } else if (inSnake(newHead[0]+'_'+(newHead[1]-1))) {
      window.clearInterval(intervalId);
      window.setTimeout(stopGame, 10*refreshRate
);
    }
    newHead[1]--;  // changes y-coordinate by -1 (going UP)
  } else if (dir == 'down') {
    if (newHead[1] == height - 1) {
      throw 'cannot move down from ' + newHead[0] + ', ' + newHead[1];
    } else if (inSnake(newHead[0]+'_'+(newHead[1]+1))) {
      window.clearInterval(intervalId);
      window.setTimeout(stopGame, 10*refreshRate
);
    }
    newHead[1]++;
  }
  snake.unshift(newHead); // add newHead to the beginning of the list
  if (snake[0][0] == food[0] && snake[0][1] == food[1]) { // if snake's head is on the food
  food = [-10, -10];
} else {
  delTail();
}
updateBoard();
}

function delTail() {  // simple function to remove the tail
  snake.pop();
}

function placeFood(x, y) {
  startGame();
  if (inSnake(x+'_'+y)) {
    console.log('Invalid food placement: Food collides with snake');
  } else if (x >= width || y >= height || x < 0 || y < 0) { // off of board
    console.log('Invalid food placement: Food is off board');
  } else {
    food = [x, y];
    console.log('Food placed at '+x+' '+y);
    updateBoard();
  }
}
<commit_msg>Fixed merge conflict<commit_after>var isMobile = false;
var snake = [[1, 0], [0, 0]]; // An array of [x, y] pairs. The head is at [0], tail is at [-1].
var board; // top left is 0, 0, bottom right is height-1, width-1
var food = [-10, -10];  // if there is no food, use this to avoid null pointers
var refreshRate = 250; // How quickly the snake moves. (ms)
var intervalId;
var gameIsStopped = true;
// high scores (both time- and food-based) for each level
var timeHighScores = [];
var pelletHighScores = [];
var currentTimeScore = 0;

if (document.cookie !== null) {
  console.log(document.cookie);
  cookies = document.cookie.split(';');
  for (var c=0; c<cookies.length; c++) {
    var cookie = cookies[c].split('=');
    console.log(cookie);
    if (cookie[0] == 'speed') {
      changeSpeed(cookie[1]);
    } else if (cookie[0] == 'highest') {
      maxLevel = cookie[1];
    }
  }
}

// Clears board & stops AI
function stopGame() {
  if ((snake.length - snake.level) > pelletHighScores[level]) {
    pelletHighScores[level] = snake.length - snake.level;
  }
  console.log("Game stopped");
  gameIsStopped = true;
  upLevel();
  board = [];
  board.length = width; // board[x][y]
  for (var i = 0; i < width; i++) {
    board[i] = [];
    board[i].length = height;
  }
  food = [-10, -10];
  snake = [];
  for (var i=0; i<level+3; i++) {
    snake.unshift([i, 0]);
  }
  dir = 'right';
  updateBoard();
}

// Snake collided with self, start next AI level
function startGame() {
  if (!gameIsStopped) {
    return;
  }
  document.cookie = 'speed='+refreshRate+'; highest='+maxLevel;
  console.log(document.cookie);
  gameIsStopped = false;
  console.log("Game started");
  console.log("Round ended! Advancing to AI level", level);
  intervalId = window.setInterval(ai, refreshRate, level); // calls ai(level) at refreshRate
  ai(level);
}

function changeSpeed(rate) {
  refreshRate = rate; // set to new value
  window.clearInterval(intervalId);
  if (!gameIsStopped) { // ensures only one interval going on at once
    intervalId = window.setInterval(ai, refreshRate, level); // calls ai(level) at refreshRate
    ai(level);
  }
}

function debugSnake() {
  for (var s=0; s<snake.length; s++) {
    console.log(snake[s][0], snake[s][1]);
  }
}

function inSnakeTwice(string) {
  var count = 0;
  for (var s=0; s<snake.length; s++) {
    if (string == snake[s][0]+'_'+snake[s][1]) {
      count++;
    }
  }
  return count==2;
}

// String is of form x_y to allow compatibility with Board.js
function inSnake(string) {
  for (var s=0; s<snake.length; s++) {
    if (string == snake[s][0]+'_'+snake[s][1]) {
      return true;
    }
  }
  return false;
}

function moveHead(dir) {
  // move snake's head in given direction
  // 'right', 'left', 'up', and 'down'
  var newHead = snake[0].slice(0); // Clone so we don't move the head by accident.
  if (dir == 'right') {
    if (newHead[0] == width - 1) {
      throw 'cannot move right from ' + newHead[0] + ', ' + newHead[1];
    } else if (inSnake((newHead[0]+1)+'_'+newHead[1])) {
      window.clearInterval(intervalId);
      window.setTimeout(stopGame, 10*refreshRate
);
    }
    newHead[0]++;
  } else if (dir == 'left') {
    if (newHead[0] === 0) {
      throw 'cannot move left from ' + newHead[0] + ', ' + newHead[1];
    } else if (inSnake((newHead[0]-1)+'_'+newHead[1])) {
      window.clearInterval(intervalId);
      window.setTimeout(stopGame, 10*refreshRate
);
    }
    newHead[0]--;
  } else if (dir == 'up') {  // add new head above current one
    if (newHead[1] === 0) {
      throw 'cannot move up from ' + newHead[0] + ', ' + newHead[1];
    } else if (inSnake(newHead[0]+'_'+(newHead[1]-1))) {
      window.clearInterval(intervalId);
      window.setTimeout(stopGame, 10*refreshRate
);
    }
    newHead[1]--;  // changes y-coordinate by -1 (going UP)
  } else if (dir == 'down') {
    if (newHead[1] == height - 1) {
      throw 'cannot move down from ' + newHead[0] + ', ' + newHead[1];
    } else if (inSnake(newHead[0]+'_'+(newHead[1]+1))) {
      window.clearInterval(intervalId);
      window.setTimeout(stopGame, 10*refreshRate
);
    }
    newHead[1]++;
  }
  snake.unshift(newHead); // add newHead to the beginning of the list
  if (snake[0][0] == food[0] && snake[0][1] == food[1]) { // if snake's head is on the food
  food = [-10, -10];
} else {
  delTail();
}
updateBoard();
}

function delTail() {  // simple function to remove the tail
  snake.pop();
}

function placeFood(x, y) {
  startGame();
  if (inSnake(x+'_'+y)) {
    console.log('Invalid food placement: Food collides with snake');
  } else if (x >= width || y >= height || x < 0 || y < 0) { // off of board
    console.log('Invalid food placement: Food is off board');
  } else {
    food = [x, y];
    updateBoard();
  }
}
<|endoftext|>


<commit_before>/**
 * Implementaion of a generic store you can populate with data
 *
 * API Short reference:
 *   Store.create(String category, Array items|Object item);
 *   Store.update(String category, Array items|Object item);
 *   Store.get(String category, String id);
 *   Store.remove(String category, Array items|Object item);
 *   Store.clean(String category);
 */
(function (global) {
  'use strict';

  /**
   * Object extend, check for extend, jQuery.extend and _.extend, otherwise
   * use own implementation
   *
   * @param  {Object} object1 Base object
   * @param  {Object} object2 Object to merge in
   * @return {Object}         Extended object
   */
  var extend = global.extend ||
      (global.jQuery && global.jQuery.extend.bind(this, true)) ||
      (global._ && global._.merge) || function (object1, object2) {
    var i;

    for (i in object2) {
       if (object2.hasOwnProperty(i)) {
         object1[i] = object2[i];
       }
    }

    return object1;
  };

  // Store with methods
  var _Store = {

    /**
     * Internal storage object to save data
     * @type {Object}
     */
    storage: {},

    /**
     * Generate unique IDs
     * Taken from http://stackoverflow.com/questions/105034/create-guid-uuid-in-javascript
     * and shortened UIDs
     * @return {String} Unique id
     */
    generateUuid: function () {
      function s4() {
        return Math.floor((1 + Math.random()) * 0x10000)
          .toString(16)
          .substring(1);
      }

      return s4() + s4() + '-' + s4() + '-' + s4();
    },

    /**
     * Find an item based on a name
     * @param  {Array}       category Category in store to find (first level)
     * @param  {String}      id       ID which you want to find
     * @return {null|Object}          Item which was found or null
     */
    find: function (category, id) {
      var item = null;
      var i = 0;

      if (!_Store.storage[category]) {
        return null;
      }

      if (typeof category !== 'string') {
        throw new Error('Store#find: Category ' + category + ' is not a string.');
      }

      if (typeof id !== 'string') {
        throw new Error('Store#find: ID ' + id + ' is not a string.');
      }

      for (; _Store.storage[category].length > i; i++) {
        if (_Store.storage[category][i].id === id) {
          item = _Store.storage[category][i];
        }
      }

      return item;
    },

    /**
     * Create a new category (child) in store
     * @param  {String}       category Name of category you want to create
     * @return {Object|false}          item, if could be created; false if category is alreay available
     */
    createCategory: function (category) {
      if (!_Store.storage[category]) {
        _Store.storage[category] = [];

        return true;
      }

      return false;
    },

    /**
     * Create a new item in store, update exisiting if that's what we're looking for
     * @param  {String}       category Category to look out for
     * @param  {Array}        items     Items that should be created
     * @return {Object|false}          Created object
     */
    create: function (category, items) {
      if (!_Store.storage[category]) {
        throw new Error('Store: Category ' + category + ' does not exist.');
      }

      // Should be an array
      if (items.constructor !== Array) {
        items = [items];
      }

      items.forEach(function (item) {

        if (!item.id) {
          item.id = _Store.generateUuid();
        }

        if (_Store.find(category, item.id)) {
          return _Store.update(category, item);
        }

        // Set an index on _Store item
        item.index = _Store.storage[category].length;

        _Store.storage[category].push(item);
      });

      return items;
    },

    /**
     * Remove item from store
     * @param  {String}  category Category to look out for
     * @param  {Array}   ids      Name of item that should be removed
     * @return {Boolean}          true, if removal was successful
     */
    remove: function (category, ids) {

      // Ensure that we use an array
      if (ids.constructor !== Array) {
        ids = [ids];
      }

      ids.forEach(function (id) {
        var storedItem = _Store.find(category, id);

        if (storedItem !== null) {
          _Store.storage[category] = _Store.storage[category].filter(function (item) {
            return item.id !== id;
          });
        }
      });

      return true;
    },

    /**
     * Update existing items in store
     * @param  {String}         category Category to look out for
     * @param  {Array|Object}   items    New items
     * @return {Object|Boolean}          True, if update was ok; false if item does not exist
     */
    update: function (category, items) {

      // Ensure that we use an array
      if (items.constructor !== Array) {
        items = [items];
      }

      // If category does not exist, throw
      if (!_Store.storage[category]) {
        throw new Error('Store: Category "' + category + '" does not exist.');
      }

      items.forEach(function (item) {
        var storedItem = _Store.find(category, item.id);

        if (storedItem !== null) {
          _Store.storage[category][storedItem.index] = extend(storedItem, item);

          return true;
        }

        return false;
      });
    },

    /**
     * Clean up a category tree
     * @param  {Array}       category Category in store to find (first level)
     * @return {Array|false}          Empty category, false if category not present
     */
    clean: function (category) {
      if (!_Store.storage[category]) {
        return false;
      }

      _Store.storage[category] = [];

      return _Store.storage[category];
    }
  };

  /**
   * API
   */
  var Store = {

    /**
     * Get item from storage, proxy for private _Store.find
     * @param  {Array}       category Category in store to find (first level)
     * @param  {String}      id       id which you want to find
     * @return {null|Object}          Item which was found or null
     */
    get: function (category, id) {
      return _Store.find(category, id);
    },

    /**
     * Create new items
     * @param  {String}       category Category to look out for
     * @param  {Array|Object} items    Item that should be created
     * @return {Object|false}          Item or false
     */
    create: function (category, items) {
      _Store.createCategory(category);
      return _Store.create(category, items);
    },

    /**
     * Update items in storage, proxy for private _Store.update
     * @param  {Array}        category Category in store to find (first level)
     * @param  {Array|Object} items    Item you want to find
     * @return {Boolean}               Whether or not update was successfull
     */
    update: function (category, items) {
      return _Store.update(category, items);
    },

    /**
     * Remove item from storage, proxy for private _Store.remove
     * @param  {Array}   category Category in store to find (first level)
     * @param  {Array}   ids      Name which you want to find
     * @return {Boolean}          Whether or not removal was successfull
     */
    remove: function (category, ids) {
      return _Store.remove(category, ids);
    },

    /**
     * Get full storage
     * @return {Object} Set full storage
     */
    getAll: function () {
      return _Store.storage;
    },

    /**
     * Get all elements from a category
     * @param  {Array} category Category in store to find (first level)
     * @return {Array}          All items in category
     */
    getAllByCategory: function (category) {
      return _Store.storage[category];
    },

    /**
     * Clean up a category
     * @param  {Array} category Category in store to find (first level)
     * @return {Array}          Empty category
     */
    clean: function (category) {
      return _Store.clean(category);
    }
  };

  // Make internal methods public for testing purposes
  if (process.env && process.env.__test) {
    Store._Store = _Store;
  }

  /*
   * AMD, module loader, global registration
   */

  // Expose loaders that implement the Node module pattern.
  if (typeof module === 'object' && module && typeof module.exports === 'object') {
    module.exports = Store;

  // Register as an AMD module
  } else if (typeof define === 'function' && define.amd) {
    define('Store', [], function () {
      return Store;
    });

  // Export into global space
  } else if (typeof global === 'object' && typeof global.document === 'object') {
    global.Store = Store;
  }
}(this));
<commit_msg>Add `process` into check for tests in order to prevent failure<commit_after>/**
 * Implementaion of a generic store you can populate with data
 *
 * API Short reference:
 *   Store.create(String category, Array items|Object item);
 *   Store.update(String category, Array items|Object item);
 *   Store.get(String category, String id);
 *   Store.remove(String category, Array items|Object item);
 *   Store.clean(String category);
 */
(function (global) {
  'use strict';

  /**
   * Object extend, check for extend, jQuery.extend and _.extend, otherwise
   * use own implementation
   *
   * @param  {Object} object1 Base object
   * @param  {Object} object2 Object to merge in
   * @return {Object}         Extended object
   */
  var extend = global.extend ||
      (global.jQuery && global.jQuery.extend.bind(this, true)) ||
      (global._ && global._.merge) || function (object1, object2) {
    var i;

    for (i in object2) {
       if (object2.hasOwnProperty(i)) {
         object1[i] = object2[i];
       }
    }

    return object1;
  };

  // Store with methods
  var _Store = {

    /**
     * Internal storage object to save data
     * @type {Object}
     */
    storage: {},

    /**
     * Generate unique IDs
     * Taken from http://stackoverflow.com/questions/105034/create-guid-uuid-in-javascript
     * and shortened UIDs
     * @return {String} Unique id
     */
    generateUuid: function () {
      function s4() {
        return Math.floor((1 + Math.random()) * 0x10000)
          .toString(16)
          .substring(1);
      }

      return s4() + s4() + '-' + s4() + '-' + s4();
    },

    /**
     * Find an item based on a name
     * @param  {Array}       category Category in store to find (first level)
     * @param  {String}      id       ID which you want to find
     * @return {null|Object}          Item which was found or null
     */
    find: function (category, id) {
      var item = null;
      var i = 0;

      if (!_Store.storage[category]) {
        return null;
      }

      if (typeof category !== 'string') {
        throw new Error('Store#find: Category ' + category + ' is not a string.');
      }

      if (typeof id !== 'string') {
        throw new Error('Store#find: ID ' + id + ' is not a string.');
      }

      for (; _Store.storage[category].length > i; i++) {
        if (_Store.storage[category][i].id === id) {
          item = _Store.storage[category][i];
        }
      }

      return item;
    },

    /**
     * Create a new category (child) in store
     * @param  {String}       category Name of category you want to create
     * @return {Object|false}          item, if could be created; false if category is alreay available
     */
    createCategory: function (category) {
      if (!_Store.storage[category]) {
        _Store.storage[category] = [];

        return true;
      }

      return false;
    },

    /**
     * Create a new item in store, update exisiting if that's what we're looking for
     * @param  {String}       category Category to look out for
     * @param  {Array}        items     Items that should be created
     * @return {Object|false}          Created object
     */
    create: function (category, items) {
      if (!_Store.storage[category]) {
        throw new Error('Store: Category ' + category + ' does not exist.');
      }

      // Should be an array
      if (items.constructor !== Array) {
        items = [items];
      }

      items.forEach(function (item) {

        if (!item.id) {
          item.id = _Store.generateUuid();
        }

        if (_Store.find(category, item.id)) {
          return _Store.update(category, item);
        }

        // Set an index on _Store item
        item.index = _Store.storage[category].length;

        _Store.storage[category].push(item);
      });

      return items;
    },

    /**
     * Remove item from store
     * @param  {String}  category Category to look out for
     * @param  {Array}   ids      Name of item that should be removed
     * @return {Boolean}          true, if removal was successful
     */
    remove: function (category, ids) {

      // Ensure that we use an array
      if (ids.constructor !== Array) {
        ids = [ids];
      }

      ids.forEach(function (id) {
        var storedItem = _Store.find(category, id);

        if (storedItem !== null) {
          _Store.storage[category] = _Store.storage[category].filter(function (item) {
            return item.id !== id;
          });
        }
      });

      return true;
    },

    /**
     * Update existing items in store
     * @param  {String}         category Category to look out for
     * @param  {Array|Object}   items    New items
     * @return {Object|Boolean}          True, if update was ok; false if item does not exist
     */
    update: function (category, items) {

      // Ensure that we use an array
      if (items.constructor !== Array) {
        items = [items];
      }

      // If category does not exist, throw
      if (!_Store.storage[category]) {
        throw new Error('Store: Category "' + category + '" does not exist.');
      }

      items.forEach(function (item) {
        var storedItem = _Store.find(category, item.id);

        if (storedItem !== null) {
          _Store.storage[category][storedItem.index] = extend(storedItem, item);

          return true;
        }

        return false;
      });
    },

    /**
     * Clean up a category tree
     * @param  {Array}       category Category in store to find (first level)
     * @return {Array|false}          Empty category, false if category not present
     */
    clean: function (category) {
      if (!_Store.storage[category]) {
        return false;
      }

      _Store.storage[category] = [];

      return _Store.storage[category];
    }
  };

  /**
   * API
   */
  var Store = {

    /**
     * Get item from storage, proxy for private _Store.find
     * @param  {Array}       category Category in store to find (first level)
     * @param  {String}      id       id which you want to find
     * @return {null|Object}          Item which was found or null
     */
    get: function (category, id) {
      return _Store.find(category, id);
    },

    /**
     * Create new items
     * @param  {String}       category Category to look out for
     * @param  {Array|Object} items    Item that should be created
     * @return {Object|false}          Item or false
     */
    create: function (category, items) {
      _Store.createCategory(category);
      return _Store.create(category, items);
    },

    /**
     * Update items in storage, proxy for private _Store.update
     * @param  {Array}        category Category in store to find (first level)
     * @param  {Array|Object} items    Item you want to find
     * @return {Boolean}               Whether or not update was successfull
     */
    update: function (category, items) {
      return _Store.update(category, items);
    },

    /**
     * Remove item from storage, proxy for private _Store.remove
     * @param  {Array}   category Category in store to find (first level)
     * @param  {Array}   ids      Name which you want to find
     * @return {Boolean}          Whether or not removal was successfull
     */
    remove: function (category, ids) {
      return _Store.remove(category, ids);
    },

    /**
     * Get full storage
     * @return {Object} Set full storage
     */
    getAll: function () {
      return _Store.storage;
    },

    /**
     * Get all elements from a category
     * @param  {Array} category Category in store to find (first level)
     * @return {Array}          All items in category
     */
    getAllByCategory: function (category) {
      return _Store.storage[category];
    },

    /**
     * Clean up a category
     * @param  {Array} category Category in store to find (first level)
     * @return {Array}          Empty category
     */
    clean: function (category) {
      return _Store.clean(category);
    }
  };

  // Make internal methods public for testing purposes
  if (process && process.env && process.env.__test) {
    Store._Store = _Store;
  }

  /*
   * AMD, module loader, global registration
   */

  // Expose loaders that implement the Node module pattern.
  if (typeof module === 'object' && module && typeof module.exports === 'object') {
    module.exports = Store;

  // Register as an AMD module
  } else if (typeof define === 'function' && define.amd) {
    define('Store', [], function () {
      return Store;
    });

  // Export into global space
  } else if (typeof global === 'object' && typeof global.document === 'object') {
    global.Store = Store;
  }
}(this));
<|endoftext|>


<commit_before>/*
Copyright 2016 First People's Cultural Council

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.
*/
import React, { Component, PropTypes } from 'react'
import Immutable from 'immutable'

// REDUX
import { connect } from 'react-redux'
// REDUX: actions/dispatch/func
import { pushWindowPath } from 'providers/redux/reducers/windowPath'
import { queryPage } from 'providers/redux/reducers/fvPage'
import { fetchUserStartpage } from 'providers/redux/reducers/fvUser'

import selectn from 'selectn'
import classNames from 'classnames'

import ProviderHelpers from 'common/ProviderHelpers'

import PromiseWrapper from 'views/components/Document/PromiseWrapper'

import RaisedButton from 'material-ui/lib/raised-button'

import IntroCardView from 'views/components/Browsing/intro-card-view'
import TextHeader from 'views/components/Document/Typography/text-header'

import { isMobile } from 'react-device-detect'
import IntlService from 'views/services/intl'
import NavigationHelpers from '../../../common/NavigationHelpers'

/**
 * Explore Archive page shows all the families in the archive
 */

const { func, object, string } = PropTypes
export class PageHome extends Component {
  static propTypes = {
    // REDUX: reducers/state
    computeLogin: object.isRequired,
    computePage: object.isRequired,
    computeUserStartpage: object.isRequired,
    properties: object.isRequired,
    windowPath: string.isRequired,
    // REDUX: actions/dispatch/func
    fetchUserStartpage: func.isRequired,
    pushWindowPath: func.isRequired,
    queryPage: func.isRequired,
  }

  /*static contextTypes = {
        muiTheme: React.object.isRequired
    };*/

  intl = IntlService.instance

  constructor(props, context) {
    super(props, context)
    window.intl = this.intl

    this.state = {
      pagePath: '/' + this.props.properties.domain + '/sections/Site/Resources/',
      dialectsPath: '/' + this.props.properties.domain + '/sections/',
    }
    ;['_onNavigateRequest', '_getBlockByArea'].forEach((method) => (this[method] = this[method].bind(this)))
  }

  componentDidMount() {
    this.props.queryPage(this.state.pagePath, " AND fvpage:url LIKE '/home/'" + '&sortOrder=ASC' + '&sortBy=dc:title')

    // Get user start page
    ProviderHelpers.fetchIfMissing('currentUser', this.props.fetchUserStartpage, this.props.computeUserStartpage)
  }

  componentDidUpdate() {
    /*
    Redirect user to their start page if they:
    - are members of a single dialect
    - have one defined,
    - are not an admin
    */

    // If user is accessing /home directly, do not redirect.
    if (this.props.windowPath.indexOf('/home') !== -1) {
      return
    }
    const _computeUserStartpage = ProviderHelpers.getEntry(this.props.computeUserStartpage, 'currentUser')
    const startPage = selectn('response.value', _computeUserStartpage)
    const isAdmin = ProviderHelpers.isAdmin(this.props.computeLogin)
    if (startPage && isAdmin === false) {
      window.location = startPage
    }
  }

  _onNavigateRequest(path) {
    this.props.pushWindowPath(path)
  }

  _getBlockByArea(page, area) {
    return (selectn('fvpage:blocks', page) || []).filter((block) => {
      return block.area == area
    })
  }

  render() {
    let bgAlign = 'center'

    if (isMobile) {
      bgAlign = 'left'
    }

    const homePageStyle = {
      position: 'relative',
      minHeight: '155px',
      backgroundAttachment: 'fixed',
      background: 'transparent url("assets/images/fv-intro-background.jpg") bottom ' + bgAlign + ' no-repeat',
      backgroundSize: 'cover',
      boxShadow: 'inset 0px 64px 112px 0 rgba(0,0,0,0.6)',
      overflow: 'hidden',
    }

    const computeEntities = Immutable.fromJS([
      {
        id: this.state.pagePath,
        entity: this.props.computePage,
      },
      {
        id: 'currentUser',
        entity: this.props.computeUserStartpage,
      },
      /*
      {
      'id': this.state.dialectsPath,
      'entity': this.props.computePortals
      }
      */
    ])

    const _computeUserStartpage = ProviderHelpers.getEntry(this.props.computeUserStartpage, 'currentUser')
    const computePage = ProviderHelpers.getEntry(this.props.computePage, this.state.pagePath)
    //const computePortals = ProviderHelpers.getEntry(this.props.computePortals, this.state.dialectsPath);

    const page = selectn('response.entries[0].properties', computePage)
    //const dialects = selectn('response.entries', computePortals);

    const primary1Color = selectn('theme.palette.baseTheme.palette.primary1Color', this.props.properties)
    const primary2Color = selectn('theme.palette.baseTheme.palette.primary2Color', this.props.properties)
    const alternateTextColor = selectn('theme.palette.baseTheme.palette.alternateTextColor', this.props.properties)
    const intl = this.intl

    const accessButtons = []

    // Compute User Registration Tasks
    ;(selectn('response.entries', _computeUserStartpage) || []).map(
      function computeUserStartPageMap(dialect) {
        const tableRow = (
          <RaisedButton
            label={'Access ' + selectn('properties.dc:title', dialect)}
            primary
            onClick={this._onNavigateRequest.bind(
              this,
              NavigationHelpers.generateStaticURL('/explore/FV/sections/Data/')
            )}
            style={{ marginRight: '10px', height: '50px' }}
            labelColor={alternateTextColor}
            labelStyle={{ fontSize: '1.34em' }}
          />
        )

        accessButtons.push(tableRow)
      }.bind(this)
    )

    if (accessButtons.length === 0) {
      accessButtons[0] = (
        <RaisedButton
          label={
            this.intl.translate({
              key: 'get_started!',
              default: 'Get Started!',
              case: 'words',
            }) + '!'
          }
          primary
          onClick={this._onNavigateRequest.bind(
            this,
            NavigationHelpers.generateStaticURL('/explore/FV/sections/Data/')
          )}
          style={{ marginRight: '10px', height: '50px' }}
          labelColor={alternateTextColor}
          labelStyle={{ fontSize: '1.34em' }}
        />
      )
    }

    return (
      <PromiseWrapper renderOnError computeEntities={computeEntities}>

                  {this.intl.searchAndReplace(selectn('fvpage:blocks[0].title', page), {})}

{accessButtons}

            {this._getBlockByArea(page, 1).map((block, i) => {
              return (

{intl.searchAndReplace(selectn('title', block))}

              )
            })}

          {this._getBlockByArea(page, 2).length > 0 ? (

              <TextHeader
                title={this.intl.translate({
                  key: ['views', 'pages', 'home', 'tools_and_resources'],
                  default: 'TOOLS & RESOURCES',
                  case: 'words',
                })}
                properties={this.props.properties}
              />

          ) : null}

            {this._getBlockByArea(page, 2).map((block, i) => {
              return (

                  <IntroCardView block={block} primary1Color={primary1Color} primary2Color={primary2Color} />

              )
            })}

          {this._getBlockByArea(page, 3).length > 0 ? (

              <TextHeader
                title={this.intl.translate({
                  key: ['views', 'pages', 'home', 'news_and_updates'],
                  default: 'NEWS & UPDATES',
                  case: 'words',
                })}
                properties={this.props.properties}
              />

          ) : null}

            {this._getBlockByArea(page, 3).map((block, i) => {
              return (

                  <IntroCardView block={block} primary1Color={primary1Color} primary2Color={primary2Color} />

              )
            })}

          {this._getBlockByArea(page, 4).length > 0 ? (

              <TextHeader
                title={this.intl.translate({
                  key: ['views', 'pages', 'home', 'compatibility'],
                  default: 'COMBATIBILITY',
                  case: 'words',
                })}
                properties={this.props.properties}
              />

          ) : null}

            {this._getBlockByArea(page, 4).map((block, i) => {
              return (

{this.intl.searchAndReplace(selectn('title', block))}

              )
            })}

      </PromiseWrapper>
    )
  }
}

// REDUX: reducers/state
const mapStateToProps = (state /*, ownProps*/) => {
  const { fvPage, fvUser, navigation, nuxeo, windowPath } = state

  const { properties } = navigation
  const { computeLogin } = nuxeo
  const { computePage } = fvPage
  const { computeUserStartpage } = fvUser
  const { _windowPath } = windowPath

  return {
    computeLogin,
    computePage,
    computeUserStartpage,
    properties,
    windowPath: _windowPath,
  }
}

// REDUX: actions/dispatch/func
const mapDispatchToProps = {
  fetchUserStartpage,
  pushWindowPath,
  queryPage,
}

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(PageHome)
<commit_msg>PageHome: Check for dif url before redirecting<commit_after>/*
Copyright 2016 First People's Cultural Council

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.
*/
import React, { Component, PropTypes } from 'react'
import Immutable from 'immutable'

// REDUX
import { connect } from 'react-redux'
// REDUX: actions/dispatch/func
import { pushWindowPath } from 'providers/redux/reducers/windowPath'
import { queryPage } from 'providers/redux/reducers/fvPage'
import { fetchUserStartpage } from 'providers/redux/reducers/fvUser'

import selectn from 'selectn'
import classNames from 'classnames'

import ProviderHelpers from 'common/ProviderHelpers'

import PromiseWrapper from 'views/components/Document/PromiseWrapper'

import RaisedButton from 'material-ui/lib/raised-button'

import IntroCardView from 'views/components/Browsing/intro-card-view'
import TextHeader from 'views/components/Document/Typography/text-header'

import { isMobile } from 'react-device-detect'
import IntlService from 'views/services/intl'
import NavigationHelpers from '../../../common/NavigationHelpers'

/**
 * Explore Archive page shows all the families in the archive
 */

const { func, object, string } = PropTypes
export class PageHome extends Component {
  static propTypes = {
    // REDUX: reducers/state
    computeLogin: object.isRequired,
    computePage: object.isRequired,
    computeUserStartpage: object.isRequired,
    properties: object.isRequired,
    windowPath: string.isRequired,
    // REDUX: actions/dispatch/func
    fetchUserStartpage: func.isRequired,
    pushWindowPath: func.isRequired,
    queryPage: func.isRequired,
  }

  /*static contextTypes = {
        muiTheme: React.object.isRequired
    };*/

  intl = IntlService.instance

  constructor(props, context) {
    super(props, context)
    window.intl = this.intl

    this.state = {
      pagePath: '/' + this.props.properties.domain + '/sections/Site/Resources/',
      dialectsPath: '/' + this.props.properties.domain + '/sections/',
    }
    ;['_onNavigateRequest', '_getBlockByArea'].forEach((method) => (this[method] = this[method].bind(this)))
  }

  componentDidMount() {
    this.props.queryPage(this.state.pagePath, " AND fvpage:url LIKE '/home/'" + '&sortOrder=ASC' + '&sortBy=dc:title')

    // Get user start page
    ProviderHelpers.fetchIfMissing('currentUser', this.props.fetchUserStartpage, this.props.computeUserStartpage)
  }

  componentDidUpdate() {
    /*
    Redirect user to their start page if they:
    - are members of a single dialect
    - have one defined,
    - are not an admin
    */

    // If user is accessing /home directly, do not redirect.
    if (this.props.windowPath.indexOf('/home') !== -1) {
      return
    }
    const _computeUserStartpage = ProviderHelpers.getEntry(this.props.computeUserStartpage, 'currentUser')
    const startPage = selectn('response.value', _computeUserStartpage)
    const isAdmin = ProviderHelpers.isAdmin(this.props.computeLogin)
    if (startPage && isAdmin === false && window.location.href !== startPage) {
      window.location = startPage
    }
  }

  _onNavigateRequest(path) {
    this.props.pushWindowPath(path)
  }

  _getBlockByArea(page, area) {
    return (selectn('fvpage:blocks', page) || []).filter((block) => {
      return block.area == area
    })
  }

  render() {
    let bgAlign = 'center'

    if (isMobile) {
      bgAlign = 'left'
    }

    const homePageStyle = {
      position: 'relative',
      minHeight: '155px',
      backgroundAttachment: 'fixed',
      background: 'transparent url("assets/images/fv-intro-background.jpg") bottom ' + bgAlign + ' no-repeat',
      backgroundSize: 'cover',
      boxShadow: 'inset 0px 64px 112px 0 rgba(0,0,0,0.6)',
      overflow: 'hidden',
    }

    const computeEntities = Immutable.fromJS([
      {
        id: this.state.pagePath,
        entity: this.props.computePage,
      },
      {
        id: 'currentUser',
        entity: this.props.computeUserStartpage,
      },
      /*
      {
      'id': this.state.dialectsPath,
      'entity': this.props.computePortals
      }
      */
    ])

    const _computeUserStartpage = ProviderHelpers.getEntry(this.props.computeUserStartpage, 'currentUser')
    const computePage = ProviderHelpers.getEntry(this.props.computePage, this.state.pagePath)
    //const computePortals = ProviderHelpers.getEntry(this.props.computePortals, this.state.dialectsPath);

    const page = selectn('response.entries[0].properties', computePage)
    //const dialects = selectn('response.entries', computePortals);

    const primary1Color = selectn('theme.palette.baseTheme.palette.primary1Color', this.props.properties)
    const primary2Color = selectn('theme.palette.baseTheme.palette.primary2Color', this.props.properties)
    const alternateTextColor = selectn('theme.palette.baseTheme.palette.alternateTextColor', this.props.properties)
    const intl = this.intl

    const accessButtons = []

    // Compute User Registration Tasks
    ;(selectn('response.entries', _computeUserStartpage) || []).map(
      function computeUserStartPageMap(dialect) {
        const tableRow = (
          <RaisedButton
            label={'Access ' + selectn('properties.dc:title', dialect)}
            primary
            onClick={this._onNavigateRequest.bind(
              this,
              NavigationHelpers.generateStaticURL('/explore/FV/sections/Data/')
            )}
            style={{ marginRight: '10px', height: '50px' }}
            labelColor={alternateTextColor}
            labelStyle={{ fontSize: '1.34em' }}
          />
        )

        accessButtons.push(tableRow)
      }.bind(this)
    )

    if (accessButtons.length === 0) {
      accessButtons[0] = (
        <RaisedButton
          label={
            this.intl.translate({
              key: 'get_started!',
              default: 'Get Started!',
              case: 'words',
            }) + '!'
          }
          primary
          onClick={this._onNavigateRequest.bind(
            this,
            NavigationHelpers.generateStaticURL('/explore/FV/sections/Data/')
          )}
          style={{ marginRight: '10px', height: '50px' }}
          labelColor={alternateTextColor}
          labelStyle={{ fontSize: '1.34em' }}
        />
      )
    }

    return (
      <PromiseWrapper renderOnError computeEntities={computeEntities}>

                  {this.intl.searchAndReplace(selectn('fvpage:blocks[0].title', page), {})}

{accessButtons}

            {this._getBlockByArea(page, 1).map((block, i) => {
              return (

{intl.searchAndReplace(selectn('title', block))}

              )
            })}

          {this._getBlockByArea(page, 2).length > 0 ? (

              <TextHeader
                title={this.intl.translate({
                  key: ['views', 'pages', 'home', 'tools_and_resources'],
                  default: 'TOOLS & RESOURCES',
                  case: 'words',
                })}
                properties={this.props.properties}
              />

          ) : null}

            {this._getBlockByArea(page, 2).map((block, i) => {
              return (

                  <IntroCardView block={block} primary1Color={primary1Color} primary2Color={primary2Color} />

              )
            })}

          {this._getBlockByArea(page, 3).length > 0 ? (

              <TextHeader
                title={this.intl.translate({
                  key: ['views', 'pages', 'home', 'news_and_updates'],
                  default: 'NEWS & UPDATES',
                  case: 'words',
                })}
                properties={this.props.properties}
              />

          ) : null}

            {this._getBlockByArea(page, 3).map((block, i) => {
              return (

                  <IntroCardView block={block} primary1Color={primary1Color} primary2Color={primary2Color} />

              )
            })}

          {this._getBlockByArea(page, 4).length > 0 ? (

              <TextHeader
                title={this.intl.translate({
                  key: ['views', 'pages', 'home', 'compatibility'],
                  default: 'COMBATIBILITY',
                  case: 'words',
                })}
                properties={this.props.properties}
              />

          ) : null}

            {this._getBlockByArea(page, 4).map((block, i) => {
              return (

{this.intl.searchAndReplace(selectn('title', block))}

              )
            })}

      </PromiseWrapper>
    )
  }
}

// REDUX: reducers/state
const mapStateToProps = (state /*, ownProps*/) => {
  const { fvPage, fvUser, navigation, nuxeo, windowPath } = state

  const { properties } = navigation
  const { computeLogin } = nuxeo
  const { computePage } = fvPage
  const { computeUserStartpage } = fvUser
  const { _windowPath } = windowPath

  return {
    computeLogin,
    computePage,
    computeUserStartpage,
    properties,
    windowPath: _windowPath,
  }
}

// REDUX: actions/dispatch/func
const mapDispatchToProps = {
  fetchUserStartpage,
  pushWindowPath,
  queryPage,
}

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(PageHome)
<|endoftext|>


<commit_before>/*global angular*/
/*jslint plusplus: true*/

( function() {'use strict';

    angular.module('em.app').controller('MyController', ['$scope', 'activeItem', 'itemsArray', 'itemsRequest', 'itemsResponse', 'notesArray', 'notesRequest', 'notesResponse', 'tagsArray', 'tasksArray', 'tasksRequest', 'tasksResponse',
    function($scope, activeItem, itemsArray, itemsRequest, itemsResponse, notesArray, notesRequest, notesResponse, tagsArray, tasksArray, tasksRequest, tasksResponse) {

      itemsRequest.getItems(function(itemsResponse) {

        itemsArray.setItems(itemsResponse.items);
        notesArray.setNotes(itemsResponse.notes);
        tagsArray.setTags(itemsResponse.tags);
        tasksArray.setTasks(itemsResponse.tasks);

        $scope.items = itemsArray.getItems();
        $scope.notes = notesArray.getNotes();
        $scope.tags = tagsArray.getTags();
        $scope.tasks = tasksArray.getTasks();

      }, function(error) {
      });

      $scope.addNewItem = function() {

        itemsRequest.putItem($scope.newItem, function(putItemResponse) {
          itemsResponse.putItemContent($scope.newItem, putItemResponse);
          itemsArray.putNewItem($scope.newItem);
          $scope.newItem = {};

        }, function(error) {
        });
      };

      $scope.setActiveItem = function(item) {
        activeItem.setItem(item);
      };

      $scope.itemToTask = function itemToTask(item) {

        tasksRequest.putExistingTask(item, function(putExistingTaskResponse) {

          $scope.completed = 'task added';
          itemsArray.removeItem(item);

          tasksResponse.putTaskContent(item, putExistingTaskResponse);
          tasksArray.putNewTask(item);

        }, function(putTaskResponse) {
        });
      };

      $scope.itemToNote = function itemToNote(item) {

        notesRequest.putExistingNote(item, function(putExistingNoteResponse) {

          $scope.completed = 'note added';
          itemsArray.removeItem(item);

          notesResponse.putNoteContent(item, putExistingNoteResponse);
          notesArray.putNewNote(item);

        }, function(putExistingNoteResponse) {
        });
      };

    }]);
  }());
<commit_msg>Item conversion and delete.<commit_after>/*global angular*/
/*jslint plusplus: true*/

( function() {'use strict';

    angular.module('em.app').controller('MyController', ['$scope', 'activeItem', 'itemsArray', 'itemsRequest', 'itemsResponse', 'notesArray', 'notesRequest', 'notesResponse', 'tagsArray', 'tasksArray', 'tasksRequest', 'tasksResponse',
    function($scope, activeItem, itemsArray, itemsRequest, itemsResponse, notesArray, notesRequest, notesResponse, tagsArray, tasksArray, tasksRequest, tasksResponse) {

      itemsRequest.getItems(function(itemsResponse) {

        itemsArray.setItems(itemsResponse.items);
        notesArray.setNotes(itemsResponse.notes);
        tagsArray.setTags(itemsResponse.tags);
        tasksArray.setTasks(itemsResponse.tasks);

        $scope.items = itemsArray.getItems();
        $scope.notes = notesArray.getNotes();
        $scope.tags = tagsArray.getTags();
        $scope.tasks = tasksArray.getTasks();

      }, function(error) {
      });

      $scope.addNewItem = function() {

        itemsRequest.putItem($scope.newItem, function(putItemResponse) {
          itemsResponse.putItemContent($scope.newItem, putItemResponse);
          itemsArray.putNewItem($scope.newItem);
          $scope.newItem = {};

        }, function(error) {
        });
      };

      $scope.setActiveItem = function(item) {
        activeItem.setItem(item);
      };

      $scope.itemToTask = function itemToTask(item) {

        tasksRequest.putExistingTask(item, function(putExistingTaskResponse) {

          $scope.completed = 'task added';
          itemsArray.removeItem(item);

          itemsRequest.deleteItem(item, function(deleteItemResponse) {
            itemsResponse.putItemContent(item, deleteItemResponse);
          }, function(deleteItemResponse) {
          });

          tasksResponse.putTaskContent(item, putExistingTaskResponse);
          tasksArray.putNewTask(item);

        }, function(putTaskResponse) {
        });
      };

      $scope.itemToNote = function itemToNote(item) {

        notesRequest.putExistingNote(item, function(putExistingNoteResponse) {

          $scope.completed = 'note added';
          itemsArray.removeItem(item);

          itemsRequest.deleteItem(item, function(deleteItemResponse) {
            itemsResponse.putItemContent(item, deleteItemResponse);
          }, function(deleteItemResponse) {
          });

          notesResponse.putNoteContent(item, putExistingNoteResponse);
          notesArray.putNewNote(item);

        }, function(putExistingNoteResponse) {
        });
      };

    }]);
  }());
<|endoftext|>