<commit_before>const MONTHS = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];

// create a reference to the notifications list in the bottom of the app; we will write database messages into this list by
//appending list items as children of this element
const note = document.getElementById('notifications');

// create an instance of a db object for us to store the IDB data in
let db;

// create a blank instance of the object that is used to transfer data into the IDB. This is mainly for reference
const newItem = [
      { taskTitle: '', hours: 0, minutes: 0, day: 0, month: '', year: 0, notified: 'no' }
    ];

// all the variables we need for the app
const taskList = document.getElementById('task-list');

const taskForm = document.getElementById('task-form');
const title = document.getElementById('title');

const hours = document.getElementById('deadline-hours');
const minutes = document.getElementById('deadline-minutes');
const day = document.getElementById('deadline-day');
const month = document.getElementById('deadline-month');
const year = document.getElementById('deadline-year');

const submit = document.getElementById('submit');

const notificationBtn = document.getElementById('enable');

// Do an initial check to see what the notification permission state is

if(Notification.permission === 'denied' || Notification.permission === 'default') {
  notificationBtn.style.display = 'block';
} else {
  notificationBtn.style.display = 'none';
}

window.onload = function() {
  note.appendChild(createListItem('App initialised.'));
  // In the following line, you should include the prefixes of implementations you want to test.
  window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
  // DON'T use 'var indexedDB = ...' if you're not in a function.
  // Moreover, you may need references to some window.IDB* objects:
  window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.msIDBTransaction;
  window.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange;
  // (Mozilla has never prefixed these objects, so we don't need window.mozIDB*)

  // Let us open our database
  const DBOpenRequest = window.indexedDB.open('toDoList', 4);

  // Gecko-only IndexedDB temp storage option:
  // var request = window.indexedDB.open('toDoList', {version: 4, storage: 'temporary'});

  // these two event handlers act on the database being opened successfully, or not
  DBOpenRequest.onerror = function(event) {
    note.appendChild(createListItem('Error loading database.'));
  };

  DBOpenRequest.onsuccess = function(event) {
    note.appendChild(createListItem('Database initialised.'));

    // store the result of opening the database in the db variable. This is used a lot below
    db = DBOpenRequest.result;

    // Run the displayData() function to populate the task list with all the to-do list data already in the IDB
    displayData();
  };

  // This event handles the event whereby a new version of the database needs to be created
  // Either one has not been created before, or a new version number has been submitted via the
  // window.indexedDB.open line above
  //it is only implemented in recent browsers
  DBOpenRequest.onupgradeneeded = function(event) {
    const db = event.target.result;

    db.onerror = function(event) {
      note.appendChild(createListItem('Error loading database.'));
    };

    // Create an objectStore for this database

    const objectStore = db.createObjectStore('toDoList', { keyPath: 'taskTitle' });

    // define what data items the objectStore will contain

    objectStore.createIndex('hours', 'hours', { unique: false });
    objectStore.createIndex('minutes', 'minutes', { unique: false });
    objectStore.createIndex('day', 'day', { unique: false });
    objectStore.createIndex('month', 'month', { unique: false });
    objectStore.createIndex('year', 'year', { unique: false });

    objectStore.createIndex('notified', 'notified', { unique: false });

    note.appendChild(createListItem('Object store created.'));
  };

  function displayData() {
    // first clear the content of the task list so that you don't get a huge long list of duplicate stuff each time
    //the display is updated.
    while (taskList.firstChild) {
      taskList.removeChild(taskList.lastChild);
    }

    // Open our object store and then get a cursor list of all the different data items in the IDB to iterate through
    const objectStore = db.transaction('toDoList').objectStore('toDoList');
    objectStore.openCursor().onsuccess = function(event) {
      const cursor = event.target.result;
        // if there is still another cursor to go, keep runing this code
      if(cursor) {

        // check which suffix the deadline day of the month needs
        if(cursor.value.day == 1 || cursor.value.day == 21 || cursor.value.day == 31) {
          daySuffix = 'st';
        } else if(cursor.value.day == 2 || cursor.value.day == 22) {
          daySuffix = 'nd';
        } else if(cursor.value.day == 3 || cursor.value.day == 23) {
          daySuffix = 'rd';
        } else {
          daySuffix = 'th';
        }

        // build the to-do list entry and put it into the list item.
        const toDoText = `${cursor.value.taskTitle} — ${cursor.value.hours}:${cursor.value.minutes}, ${cursor.value.month} ${cursor.value.day}${daySuffix}  ${cursor.value.year}.`;
        const listItem = createListItem(toDoText);

        if(cursor.value.notified == 'yes') {
          listItem.style.textDecoration = 'line-through';
          listItem.style.color = 'rgba(255,0,0,0.5)';
        }

        // put the item item inside the task list
        taskList.appendChild(listItem);

        // create a delete button inside each list item, giving it an event handler so that it runs the deleteButton()
        // function when clicked
        const deleteButton = document.createElement('button');
        listItem.appendChild(deleteButton);
        deleteButton.textContent = 'X';
        // here we are setting a data attribute on our delete button to say what task we want deleted if it is clicked!
        deleteButton.setAttribute('data-task', cursor.value.taskTitle);
        deleteButton.onclick = function(event) {
          deleteItem(event);
        }

        // continue on to the next item in the cursor
        cursor.continue();

      // if there are no more cursor items to iterate through, say so, and exit the function
      } else {
        note.appendChild(createListItem('Entries all displayed.'));
      }
    }
  }

  // give the form submit button an event listener so that when the form is submitted the addData() function is run
  taskForm.addEventListener('submit',addData,false);

  function addData(e) {
    // prevent default - we don't want the form to submit in the conventional way
    e.preventDefault();

    // Stop the form submitting if any values are left empty. This is just for browsers that don't support the HTML5 form
    // required attributes
    if(title.value == '' || hours.value == null || minutes.value == null || day.value == '' || month.value == '' || year.value == null) {
      note.appendChild(createListItem('Data not submitted — form incomplete.'));
      return;
    } else {

      // grab the values entered into the form fields and store them in an object ready for being inserted into the IDB
      const newItem = [
        { taskTitle: title.value, hours: hours.value, minutes: minutes.value, day: day.value, month: month.value, year: year.value, notified: 'no' }
      ];

      // open a read/write db transaction, ready for adding the data
      const transaction = db.transaction(['toDoList'], 'readwrite');

      // report on the success of the transaction completing, when everything is done
      transaction.oncomplete = function() {
        note.appendChild(createListItem('Transaction completed: database modification finished.'));

        // update the display of data to show the newly added item, by running displayData() again.
        displayData();
      };

      transaction.onerror = function() {
        note.appendChild(createListItem(`Transaction not opened due to error: ${transaction.error}`));
      };

      // call an object store that's already been added to the database
      const objectStore = transaction.objectStore('toDoList');
      console.log(objectStore.indexNames);
      console.log(objectStore.keyPath);
      console.log(objectStore.name);
      console.log(objectStore.transaction);
      console.log(objectStore.autoIncrement);

      // Make a request to add our newItem object to the object store
      const objectStoreRequest = objectStore.add(newItem[0]);
      objectStoreRequest.onsuccess = function(event) {

        // report the success of our request
        // (to detect whether it has been succesfully
        // added to the database, you'd look at transaction.oncomplete)
        note.appendChild(createListItem('Request successful.'));

        // clear the form, ready for adding the next entry
        title.value = '';
        hours.value = null;
        minutes.value = null;
        day.value = 01;
        month.value = 'January';
        year.value = 2020;
      };
    };
  };

  function deleteItem(event) {
    // retrieve the name of the task we want to delete
    const dataTask = event.target.getAttribute('data-task');

    // open a database transaction and delete the task, finding it by the name we retrieved above
    const transaction = db.transaction(['toDoList'], 'readwrite');
    transaction.objectStore('toDoList').delete(dataTask);

    // report that the data item has been deleted
    transaction.oncomplete = function() {
      // delete the parent of the button, which is the list item, so it no longer is displayed
      event.target.parentNode.parentNode.removeChild(event.target.parentNode);
      note.appendChild(createListItem(`Task "${dataTask}" deleted.`));
    };
  };

  // this function checks whether the deadline for each task is up or not, and responds appropriately
  function checkDeadlines() {
    // First of all check whether notifications are enabled or denied
    if(Notification.permission === 'denied' || Notification.permission === 'default') {
      notificationBtn.style.display = 'block';
    } else {
      notificationBtn.style.display = 'none';
    }

    // grab the time and date right now
    const now = new Date();

    // from the now variable, store the current minutes, hours, day of the month (getDate is needed for this, as getDay
    // returns the day of the week, 1-7), month, year (getFullYear needed; getYear is deprecated, and returns a weird value
    // that is not much use to anyone!) and seconds
    const minuteCheck = now.getMinutes();
    const hourCheck = now.getHours();
    const dayCheck = now.getDate();
    const monthCheck = now.getMonth();
    const yearCheck = now.getFullYear();

    // again, open a transaction then a cursor to iterate through all the data items in the IDB
    const objectStore = db.transaction(['toDoList'], 'readwrite').objectStore('toDoList');
    objectStore.openCursor().onsuccess = function(event) {
      const cursor = event.target.result;
      if(cursor) {

      // convert the month names we have installed in the IDB into a month number that JavaScript will understand.
      // The JavaScript date object creates month values as a number between 0 and 11.
      const monthNumber = MONTHS.indexOf(cursor.value.month);
      if (monthNumber === -1) alert('Incorrect month entered in database.');

        // check if the current hours, minutes, day, month and year values match the stored values for each task in the IDB.
        // The + operator in this case converts numbers with leading zeros into their non leading zero equivalents, so e.g.
        // 09 -> 9. This is needed because JS date number values never have leading zeros, but our data might.
        // The secondsCheck = 0 check is so that you don't get duplicate notifications for the same task. The notification
        // will only appear when the seconds is 0, meaning that you won't get more than one notification for each task
        if(+(cursor.value.hours) == hourCheck && +(cursor.value.minutes) == minuteCheck && +(cursor.value.day) == dayCheck && monthNumber == monthCheck && cursor.value.year == yearCheck && cursor.value.notified == 'no') {

          // If the numbers all do match, run the createNotification() function to create a system notification
          // but only if the permission is set

          if(Notification.permission === 'granted') {
            createNotification(cursor.value.taskTitle);
          }
        }

        // move on and perform the same deadline check on the next cursor item
        cursor.continue();
      }
    }
  }

  // askNotificationPermission function to ask for permission when the 'Enable notifications' button is clicked

  function askNotificationPermission() {
    // function to actually ask the permissions
    function handlePermission(permission) {
      // Whatever the user answers, we make sure Chrome stores the information
      if(!('permission' in Notification)) {
        Notification.permission = permission;
      }

      // set the button to shown or hidden, depending on what the user answers
      if(Notification.permission === 'denied' || Notification.permission === 'default') {
        notificationBtn.style.display = 'block';
      } else {
        notificationBtn.style.display = 'none';
      }
    }

    // Let's check if the browser supports notifications
    if (!'Notification' in window) {
      console.log('This browser does not support notifications.');
    } else {
      if(checkNotificationPromise()) {
        Notification.requestPermission()
        .then((permission) => {
          handlePermission(permission);
        })
      } else {
        Notification.requestPermission(function(permission) {
          handlePermission(permission);
        });
      }
    }
  }

  // Function to check whether browser supports the promise version of requestPermission()
  // Safari only supports the old callback-based version
  function checkNotificationPromise() {
    try {
      Notification.requestPermission().then();
    } catch(e) {
      return false;
    }

    return true;
  }

  // wire up notification permission functionality to 'Enable notifications' button

  notificationBtn.addEventListener('click', askNotificationPermission);

  function createListItem(contents) {
    const listItem = document.createElement('li');
    listItem.textContent = contents;
    return listItem;
  }

  // function for creating the notification
  function createNotification(title) {

    // Create and show the notification
    const img = '/to-do-notifications/img/icon-128.png';
    const text = 'HEY! Your task "' + title + '" is now overdue.';
    const notification = new Notification('To do list', { body: text, icon: img });

    // we need to update the value of notified to 'yes' in this particular data object, so the
    // notification won't be set off on it again

    // first open up a transaction as usual
    const objectStore = db.transaction(['toDoList'], 'readwrite').objectStore('toDoList');

    // get the to-do list object that has this title as it's title
    const objectStoreTitleRequest = objectStore.get(title);

    objectStoreTitleRequest.onsuccess = function() {
      // grab the data object returned as the result
      const data = objectStoreTitleRequest.result;

      // update the notified value in the object to 'yes'
      data.notified = 'yes';

      // create another request that inserts the item back into the database
      const updateTitleRequest = objectStore.put(data);

      // when this new request succeeds, run the displayData() function again to update the display
      updateTitleRequest.onsuccess = function() {
        displayData();
      }
    }
  }

  // using a setInterval to run the checkDeadlines() function every second
  setInterval(checkDeadlines, 1000);
}
<commit_msg>invert if condition to reduce nesting<commit_after>const MONTHS = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];

// create a reference to the notifications list in the bottom of the app; we will write database messages into this list by
//appending list items as children of this element
const note = document.getElementById('notifications');

// create an instance of a db object for us to store the IDB data in
let db;

// create a blank instance of the object that is used to transfer data into the IDB. This is mainly for reference
const newItem = [
      { taskTitle: '', hours: 0, minutes: 0, day: 0, month: '', year: 0, notified: 'no' }
    ];

// all the variables we need for the app
const taskList = document.getElementById('task-list');

const taskForm = document.getElementById('task-form');
const title = document.getElementById('title');

const hours = document.getElementById('deadline-hours');
const minutes = document.getElementById('deadline-minutes');
const day = document.getElementById('deadline-day');
const month = document.getElementById('deadline-month');
const year = document.getElementById('deadline-year');

const submit = document.getElementById('submit');

const notificationBtn = document.getElementById('enable');

// Do an initial check to see what the notification permission state is

if(Notification.permission === 'denied' || Notification.permission === 'default') {
  notificationBtn.style.display = 'block';
} else {
  notificationBtn.style.display = 'none';
}

window.onload = function() {
  note.appendChild(createListItem('App initialised.'));
  // In the following line, you should include the prefixes of implementations you want to test.
  window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
  // DON'T use 'var indexedDB = ...' if you're not in a function.
  // Moreover, you may need references to some window.IDB* objects:
  window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.msIDBTransaction;
  window.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange;
  // (Mozilla has never prefixed these objects, so we don't need window.mozIDB*)

  // Let us open our database
  const DBOpenRequest = window.indexedDB.open('toDoList', 4);

  // Gecko-only IndexedDB temp storage option:
  // var request = window.indexedDB.open('toDoList', {version: 4, storage: 'temporary'});

  // these two event handlers act on the database being opened successfully, or not
  DBOpenRequest.onerror = function(event) {
    note.appendChild(createListItem('Error loading database.'));
  };

  DBOpenRequest.onsuccess = function(event) {
    note.appendChild(createListItem('Database initialised.'));

    // store the result of opening the database in the db variable. This is used a lot below
    db = DBOpenRequest.result;

    // Run the displayData() function to populate the task list with all the to-do list data already in the IDB
    displayData();
  };

  // This event handles the event whereby a new version of the database needs to be created
  // Either one has not been created before, or a new version number has been submitted via the
  // window.indexedDB.open line above
  //it is only implemented in recent browsers
  DBOpenRequest.onupgradeneeded = function(event) {
    const db = event.target.result;

    db.onerror = function(event) {
      note.appendChild(createListItem('Error loading database.'));
    };

    // Create an objectStore for this database

    const objectStore = db.createObjectStore('toDoList', { keyPath: 'taskTitle' });

    // define what data items the objectStore will contain

    objectStore.createIndex('hours', 'hours', { unique: false });
    objectStore.createIndex('minutes', 'minutes', { unique: false });
    objectStore.createIndex('day', 'day', { unique: false });
    objectStore.createIndex('month', 'month', { unique: false });
    objectStore.createIndex('year', 'year', { unique: false });

    objectStore.createIndex('notified', 'notified', { unique: false });

    note.appendChild(createListItem('Object store created.'));
  };

  function displayData() {
    // first clear the content of the task list so that you don't get a huge long list of duplicate stuff each time
    //the display is updated.
    while (taskList.firstChild) {
      taskList.removeChild(taskList.lastChild);
    }

    // Open our object store and then get a cursor list of all the different data items in the IDB to iterate through
    const objectStore = db.transaction('toDoList').objectStore('toDoList');
    objectStore.openCursor().onsuccess = function(event) {
      const cursor = event.target.result;
      // if there are no (more) cursor items to iterate through, say so, and exit the function
      if(!cursor) {
        note.appendChild(createListItem('Entries all displayed.'));
        return;
      }
      // if there is still another cursor to go, keep runing this code
      // check which suffix the deadline day of the month needs
      if(cursor.value.day == 1 || cursor.value.day == 21 || cursor.value.day == 31) {
        daySuffix = 'st';
      } else if(cursor.value.day == 2 || cursor.value.day == 22) {
        daySuffix = 'nd';
      } else if(cursor.value.day == 3 || cursor.value.day == 23) {
        daySuffix = 'rd';
      } else {
        daySuffix = 'th';
      }

      // build the to-do list entry and put it into the list item.
      const toDoText = `${cursor.value.taskTitle} — ${cursor.value.hours}:${cursor.value.minutes}, ${cursor.value.month} ${cursor.value.day}${daySuffix}  ${cursor.value.year}.`;
      const listItem = createListItem(toDoText);

      if(cursor.value.notified == 'yes') {
        listItem.style.textDecoration = 'line-through';
        listItem.style.color = 'rgba(255,0,0,0.5)';
      }

      // put the item item inside the task list
      taskList.appendChild(listItem);

      // create a delete button inside each list item, giving it an event handler so that it runs the deleteButton()
      // function when clicked
      const deleteButton = document.createElement('button');
      listItem.appendChild(deleteButton);
      deleteButton.textContent = 'X';
      // here we are setting a data attribute on our delete button to say what task we want deleted if it is clicked!
      deleteButton.setAttribute('data-task', cursor.value.taskTitle);
      deleteButton.onclick = function(event) {
        deleteItem(event);
      }

      // continue on to the next item in the cursor
      cursor.continue();
    }
  }

  // give the form submit button an event listener so that when the form is submitted the addData() function is run
  taskForm.addEventListener('submit',addData,false);

  function addData(e) {
    // prevent default - we don't want the form to submit in the conventional way
    e.preventDefault();

    // Stop the form submitting if any values are left empty. This is just for browsers that don't support the HTML5 form
    // required attributes
    if(title.value == '' || hours.value == null || minutes.value == null || day.value == '' || month.value == '' || year.value == null) {
      note.appendChild(createListItem('Data not submitted — form incomplete.'));
      return;
    } else {

      // grab the values entered into the form fields and store them in an object ready for being inserted into the IDB
      const newItem = [
        { taskTitle: title.value, hours: hours.value, minutes: minutes.value, day: day.value, month: month.value, year: year.value, notified: 'no' }
      ];

      // open a read/write db transaction, ready for adding the data
      const transaction = db.transaction(['toDoList'], 'readwrite');

      // report on the success of the transaction completing, when everything is done
      transaction.oncomplete = function() {
        note.appendChild(createListItem('Transaction completed: database modification finished.'));

        // update the display of data to show the newly added item, by running displayData() again.
        displayData();
      };

      transaction.onerror = function() {
        note.appendChild(createListItem(`Transaction not opened due to error: ${transaction.error}`));
      };

      // call an object store that's already been added to the database
      const objectStore = transaction.objectStore('toDoList');
      console.log(objectStore.indexNames);
      console.log(objectStore.keyPath);
      console.log(objectStore.name);
      console.log(objectStore.transaction);
      console.log(objectStore.autoIncrement);

      // Make a request to add our newItem object to the object store
      const objectStoreRequest = objectStore.add(newItem[0]);
      objectStoreRequest.onsuccess = function(event) {

        // report the success of our request
        // (to detect whether it has been succesfully
        // added to the database, you'd look at transaction.oncomplete)
        note.appendChild(createListItem('Request successful.'));

        // clear the form, ready for adding the next entry
        title.value = '';
        hours.value = null;
        minutes.value = null;
        day.value = 01;
        month.value = 'January';
        year.value = 2020;
      };
    };
  };

  function deleteItem(event) {
    // retrieve the name of the task we want to delete
    const dataTask = event.target.getAttribute('data-task');

    // open a database transaction and delete the task, finding it by the name we retrieved above
    const transaction = db.transaction(['toDoList'], 'readwrite');
    transaction.objectStore('toDoList').delete(dataTask);

    // report that the data item has been deleted
    transaction.oncomplete = function() {
      // delete the parent of the button, which is the list item, so it no longer is displayed
      event.target.parentNode.parentNode.removeChild(event.target.parentNode);
      note.appendChild(createListItem(`Task "${dataTask}" deleted.`));
    };
  };

  // this function checks whether the deadline for each task is up or not, and responds appropriately
  function checkDeadlines() {
    // First of all check whether notifications are enabled or denied
    if(Notification.permission === 'denied' || Notification.permission === 'default') {
      notificationBtn.style.display = 'block';
    } else {
      notificationBtn.style.display = 'none';
    }

    // grab the time and date right now
    const now = new Date();

    // from the now variable, store the current minutes, hours, day of the month (getDate is needed for this, as getDay
    // returns the day of the week, 1-7), month, year (getFullYear needed; getYear is deprecated, and returns a weird value
    // that is not much use to anyone!) and seconds
    const minuteCheck = now.getMinutes();
    const hourCheck = now.getHours();
    const dayCheck = now.getDate();
    const monthCheck = now.getMonth();
    const yearCheck = now.getFullYear();

    // again, open a transaction then a cursor to iterate through all the data items in the IDB
    const objectStore = db.transaction(['toDoList'], 'readwrite').objectStore('toDoList');
    objectStore.openCursor().onsuccess = function(event) {
      const cursor = event.target.result;
      if(!cursor) return;

      // convert the month names we have installed in the IDB into a month number that JavaScript will understand.
      // The JavaScript date object creates month values as a number between 0 and 11.
      const monthNumber = MONTHS.indexOf(cursor.value.month);
      if (monthNumber === -1) alert('Incorrect month entered in database.');

      // check if the current hours, minutes, day, month and year values match the stored values for each task in the IDB.
      // The + operator in this case converts numbers with leading zeros into their non leading zero equivalents, so e.g.
      // 09 -> 9. This is needed because JS date number values never have leading zeros, but our data might.
      // The secondsCheck = 0 check is so that you don't get duplicate notifications for the same task. The notification
      // will only appear when the seconds is 0, meaning that you won't get more than one notification for each task
      if(+(cursor.value.hours) == hourCheck && +(cursor.value.minutes) == minuteCheck && +(cursor.value.day) == dayCheck && monthNumber == monthCheck && cursor.value.year == yearCheck && cursor.value.notified == 'no') {

        // If the numbers all do match, run the createNotification() function to create a system notification
        // but only if the permission is set

        if(Notification.permission === 'granted') {
          createNotification(cursor.value.taskTitle);
        }
      }

      // move on and perform the same deadline check on the next cursor item
      cursor.continue();
    }
  }

  // askNotificationPermission function to ask for permission when the 'Enable notifications' button is clicked

  function askNotificationPermission() {
    // function to actually ask the permissions
    function handlePermission(permission) {
      // Whatever the user answers, we make sure Chrome stores the information
      if(!('permission' in Notification)) {
        Notification.permission = permission;
      }

      // set the button to shown or hidden, depending on what the user answers
      if(Notification.permission === 'denied' || Notification.permission === 'default') {
        notificationBtn.style.display = 'block';
      } else {
        notificationBtn.style.display = 'none';
      }
    }

    // Let's check if the browser supports notifications
    if (!'Notification' in window) {
      console.log('This browser does not support notifications.');
    } else {
      if(checkNotificationPromise()) {
        Notification.requestPermission()
        .then((permission) => {
          handlePermission(permission);
        })
      } else {
        Notification.requestPermission(function(permission) {
          handlePermission(permission);
        });
      }
    }
  }

  // Function to check whether browser supports the promise version of requestPermission()
  // Safari only supports the old callback-based version
  function checkNotificationPromise() {
    try {
      Notification.requestPermission().then();
    } catch(e) {
      return false;
    }

    return true;
  }

  // wire up notification permission functionality to 'Enable notifications' button

  notificationBtn.addEventListener('click', askNotificationPermission);

  function createListItem(contents) {
    const listItem = document.createElement('li');
    listItem.textContent = contents;
    return listItem;
  }

  // function for creating the notification
  function createNotification(title) {

    // Create and show the notification
    const img = '/to-do-notifications/img/icon-128.png';
    const text = 'HEY! Your task "' + title + '" is now overdue.';
    const notification = new Notification('To do list', { body: text, icon: img });

    // we need to update the value of notified to 'yes' in this particular data object, so the
    // notification won't be set off on it again

    // first open up a transaction as usual
    const objectStore = db.transaction(['toDoList'], 'readwrite').objectStore('toDoList');

    // get the to-do list object that has this title as it's title
    const objectStoreTitleRequest = objectStore.get(title);

    objectStoreTitleRequest.onsuccess = function() {
      // grab the data object returned as the result
      const data = objectStoreTitleRequest.result;

      // update the notified value in the object to 'yes'
      data.notified = 'yes';

      // create another request that inserts the item back into the database
      const updateTitleRequest = objectStore.put(data);

      // when this new request succeeds, run the displayData() function again to update the display
      updateTitleRequest.onsuccess = function() {
        displayData();
      }
    }
  }

  // using a setInterval to run the checkDeadlines() function every second
  setInterval(checkDeadlines, 1000);
}
<|endoftext|>


<commit_before>var animationDuration = 1800;
var fadeInDelay = animationDuration;

jQuery.fn.extend({
  fadeInOpacity: function() {
    $( this ).animate( { opacity: 1 }, animationDuration );
  }
});

$( document ).ready( function() {
  // Fade in the whole shebang after the DOM is ready
  $( '#wrapper ').fadeIn( animationDuration, function() {

    var numberOfContactItems = 0;
    $( '.contact-item' ).each( function( i ) {
      var item = $( this );
      setTimeout( function() {
        item.fadeInOpacity();
      }, i * fadeInDelay);
      numberOfContactItems += 1;
    });

    setTimeout( function() {
      $( '.category-item' ).first().click();
    }, numberOfContactItems * fadeInDelay);

    setTimeout( function() {
      $( '.scroll-down-icon > .fa' ).addClass('animated fadeInDown');
    }, numberOfContactItems * fadeInDelay + 5000);
  });

  $('.category-grid').on('click', '.category-item', function( e ) {
    var $categoryItem = $( this ).closest('.category-item');
    $categoryItem.toggleClass( 'open closed' );
    $categoryItem.find( '.category-text' ).stop().slideToggle();
    $categoryItem.find( '.read-more-icon' ).toggleClass( 'fa-chevron-circle-down fa-times-circle open closed' );
  }).find('.category-text').on('click', function( e ) {
    e.stopPropagation();
  });

  // Scroll smoothly to element with id matching clicked element
  $( '.scroll-down-icon' ).click(function( e ) {
    $( 'html, body' ).animate({ scrollTop: $( '#' + $(this).attr('data-target') ).offset().top }, animationDuration/2);
  });

  // Set copyright year to current year
  $( '#current-year' ).text( (new Date).getFullYear() );
});
<commit_msg>Only open first category-item if not already open<commit_after>var animationDuration = 1800;
var fadeInDelay = animationDuration;

jQuery.fn.extend({
  fadeInOpacity: function() {
    $( this ).animate( { opacity: 1 }, animationDuration );
  }
});

$( document ).ready( function() {
  // Fade in the whole shebang after the DOM is ready
  $( '#wrapper ').fadeIn( animationDuration, function() {

    var numberOfContactItems = 0;
    $( '.contact-item' ).each( function( i ) {
      var item = $( this );
      setTimeout( function() {
        item.fadeInOpacity();
      }, i * fadeInDelay);
      numberOfContactItems += 1;
    });

    setTimeout( function() {
      $( '.category-item' ).first().not( '.open' ).click();
    }, numberOfContactItems * fadeInDelay);

    setTimeout( function() {
      $( '.scroll-down-icon > .fa' ).addClass('animated fadeInDown');
    }, numberOfContactItems * fadeInDelay + 5000);
  });

  $('.category-grid').on('click', '.category-item', function( e ) {
    var $categoryItem = $( this ).closest('.category-item');
    $categoryItem.toggleClass( 'open closed' );
    $categoryItem.find( '.category-text' ).stop().slideToggle();
    $categoryItem.find( '.read-more-icon' ).toggleClass( 'fa-chevron-circle-down fa-times-circle open closed' );
  }).find('.category-text').on('click', function( e ) {
    e.stopPropagation();
  });

  // Scroll smoothly to element with id matching clicked element
  $( '.scroll-down-icon' ).click(function( e ) {
    $( 'html, body' ).animate({ scrollTop: $( '#' + $(this).attr('data-target') ).offset().top }, animationDuration/2);
  });

  // Set copyright year to current year
  $( '#current-year' ).text( (new Date).getFullYear() );
});
<|endoftext|>


<commit_before>console.log("JS initialized");

//Prevent bounce effect on iOS
document.ontouchmove = function(event){
    event.preventDefault();
}

var getMetarButton = document.getElementById("getMetarButton"), useLocationButton = document.getElementById("useLocationButton"), returnButton = document.getElementById("returnButton");

// Set up a listener for given station identifier button click
getMetarButton.addEventListener('click', function(event) {
	var station = document.getElementById("stationIdentifier").value;
	document.getElementById("main-instruction").style.display = "none";
	document.getElementById("stationIdentifier").style.display = "none";
	document.getElementById("getMetarButton").style.display = "none";
	document.getElementById("useLocationButton").style.display = "none";
	fetchMetar(station);
});

// Set up event listener for using location button click
useLocationButton.addEventListener('click', function(event) {
	console.log("The use location button has been pressed!");
});

// Set up even listener for using the return button click
returnButton.addEventListener('click', function(event) {
	document.getElementById("main-instruction").style.display = "block";
	document.getElementById("stationIdentifier").style.display = "inline-block";
	document.getElementById("getMetarButton").style.display = "inline-block";
	document.getElementById("useLocationButton").style.display = "inline-block";
	document.getElementById("returnButton").style.display = "none";
	document.getElementById("raw").parentNode.removeChild(document.getElementById("raw"));
})

// A function to fetch things from a server
function request(URL) {
	var xmlRequest = new XMLHttpRequest(); // Gets stuff from server

	xmlRequest.open("GET", URL, true); // Initialize and send the request
	xmlRequest.send();

	var resultPromise = new Promise( // Because fetching METAR is async, promise to return value when fetched
		function(resolve, reject) {
			xmlRequest.onreadystatechange = function() { // Fires on ready state change; if the request is done, then return response
				if (this.readyState == XMLHttpRequest.DONE) {
					resolve(xmlRequest.response);
				}
			};
		})

	return resultPromise; // Return this promise
}

function resetElements() {
	document.getElementById("main-instruction").style.display = "block";
	document.getElementById("stationIdentifier").style.display = "inline-block";
	document.getElementById("getMetarButton").style.display = "inline-block";
	document.getElementById("useLocationButton").style.display = "block";
}

// Function to get METAR provided station identifer
function fetchMetar(station) {

	var URL = "http://avwx.rest/api/metar/" + station; // This is the URL

	var raw = document.createElement('div'); // This creates a new div to display the raw METAR
	raw.id = "raw";
	request(URL).then(function(result) { // Wait for promise to be fulfilled, and then do things with the response
		metar = JSON.parse(result); // Parse JSON

		if (metar["Raw-Report"] !== undefined) { // If there is a raw-report field in the JSON, then show that in the text
			raw.innerHTML = metar["Raw-Report"] + "

";
		} else { // If there isn't, tell the user that their query was invalid
			raw.innerHTML = "Your request was invalid!";
		}

		document.getElementById("location").appendChild(raw); // Add to the webpage!
		document.getElementById("returnButton").style.display = "inline-block"; // Show the return button
	}).catch(function(reason) { // This means that the query was rejected for some reason
		console.log(reason); // Log the reason and tell the user
		raw.innerHTML = "Your request was invalid!" + "

";
		document.getElementById("location").appendChild(raw);
		document.getElementById("returnButton").style.display = "inline-block";
    });
}<commit_msg>Allow scrolling<commit_after>console.log("JS initialized");

var getMetarButton = document.getElementById("getMetarButton"), useLocationButton = document.getElementById("useLocationButton"), returnButton = document.getElementById("returnButton");

// Set up a listener for given station identifier button click
getMetarButton.addEventListener('click', function(event) {
	var station = document.getElementById("stationIdentifier").value;
	document.getElementById("main-instruction").style.display = "none";
	document.getElementById("stationIdentifier").style.display = "none";
	document.getElementById("getMetarButton").style.display = "none";
	document.getElementById("useLocationButton").style.display = "none";
	fetchMetar(station);
});

// Set up event listener for using location button click
useLocationButton.addEventListener('click', function(event) {
	console.log("The use location button has been pressed!");
});

// Set up even listener for using the return button click
returnButton.addEventListener('click', function(event) {
	document.getElementById("main-instruction").style.display = "block";
	document.getElementById("stationIdentifier").style.display = "inline-block";
	document.getElementById("getMetarButton").style.display = "inline-block";
	document.getElementById("useLocationButton").style.display = "inline-block";
	document.getElementById("returnButton").style.display = "none";
	document.getElementById("raw").parentNode.removeChild(document.getElementById("raw"));
})

// A function to fetch things from a server
function request(URL) {
	var xmlRequest = new XMLHttpRequest(); // Gets stuff from server

	xmlRequest.open("GET", URL, true); // Initialize and send the request
	xmlRequest.send();

	var resultPromise = new Promise( // Because fetching METAR is async, promise to return value when fetched
		function(resolve, reject) {
			xmlRequest.onreadystatechange = function() { // Fires on ready state change; if the request is done, then return response
				if (this.readyState == XMLHttpRequest.DONE) {
					resolve(xmlRequest.response);
				}
			};
		})

	return resultPromise; // Return this promise
}

function resetElements() {
	document.getElementById("main-instruction").style.display = "block";
	document.getElementById("stationIdentifier").style.display = "inline-block";
	document.getElementById("getMetarButton").style.display = "inline-block";
	document.getElementById("useLocationButton").style.display = "block";
}

// Function to get METAR provided station identifer
function fetchMetar(station) {

	var URL = "http://avwx.rest/api/metar/" + station; // This is the URL

	var raw = document.createElement('div'); // This creates a new div to display the raw METAR
	raw.id = "raw";
	request(URL).then(function(result) { // Wait for promise to be fulfilled, and then do things with the response
		metar = JSON.parse(result); // Parse JSON

		if (metar["Raw-Report"] !== undefined) { // If there is a raw-report field in the JSON, then show that in the text
			raw.innerHTML = metar["Raw-Report"] + "

";
		} else { // If there isn't, tell the user that their query was invalid
			raw.innerHTML = "Your request was invalid!";
		}

		document.getElementById("location").appendChild(raw); // Add to the webpage!
		document.getElementById("returnButton").style.display = "inline-block"; // Show the return button
	}).catch(function(reason) { // This means that the query was rejected for some reason
		console.log(reason); // Log the reason and tell the user
		raw.innerHTML = "Your request was invalid!" + "

";
		document.getElementById("location").appendChild(raw);
		document.getElementById("returnButton").style.display = "inline-block";
    });
}<|endoftext|>


<commit_before>/**
 * Created by Matthew on 10/21/15.
 */

//Get canvas's element.  Used for drawing the grids and whatnot
map = document.getElementById("map");
var ctx = map.getContext("2d");

mapContainer = document.getElementById("mapContainer");

//length of edge, or radius from center to vertex.
var hexRadius = 30;

//viewport variables.  Default to geographic 0,0 being middle of screen.
var viewX = 0 - map.width/2;
var viewY = 0 - map.height/2;

//movement speed, pixels per frame that the viewport moves when you press the movement buttons.
var moveSpeed = 3;

//When moving (between mousedown and mouseup) store movement vector in these two variables.
var currentViewMovementX = 0;
var currentViewMovementY = 0;

/**
 * Draws a hexagon centered at the given x/y coordinates.
 * @param ctx
 * @param x
 * @param y
 */
function drawHex(ctx, x, y) {
    var a = (Math.PI * 2)/6;
    ctx.beginPath();
    ctx.translate(x - viewX,y - viewY); //offset by the viewport here
    ctx.rotate(0);
    ctx.moveTo(hexRadius,0);
    for (var i = 1; i < 6; i++) {
        ctx.lineTo(hexRadius*Math.cos(a * i), hexRadius*Math.sin(a * i));
    }
    ctx.closePath();
    ctx.fillStyle = "#B8E68A";
    ctx.strokeStyle = "#000000";
    ctx.stroke();
    ctx.fill();
    //Undo translation.  Is this the way I want to do it though?  It's a little dirty...
    ctx.translate(0 - (x - viewX), 0 - (y - viewY)); //reset the context back to the original position.
}

/**
 * Takes in cubic coordinates.  Converts them to cartesian, then draws a hexagon there.
 * @param x
 * @param y
 * @param z
 */
function drawHexFromCubic(x,y,z) {
    var coords = getCartFromCubic(x,y,z);
    drawHex(ctx, coords.x, coords.y);
}

/**
 * Takes in cubic coordinates, verifies they are reasonable, and returns a cartesian coordinate pair.
 * Coordinates centered around (0,0,0)/(0,0) point.
 * @param x
 * @param y
 * @param z
 * @returns {{x: number, y: number}}
 */
function getCartFromCubic(x,y,z) {
    if(x + y + z != 0) {
        console.log("Error:  your cubic coordinates don't add up.");
    }
    var cartX = x * 1.5 * hexRadius;
    var cartY = Math.sqrt(3) * ((x/2) + z) * hexRadius;
    return {x: cartX, y: cartY}; //object with .x and .y coordinates
}

//Setup event listeners for the movement buttons
document.getElementById("westButton").addEventListener("mousedown", moveViewWest);
document.getElementById("eastButton").addEventListener("mousedown", moveViewEast);
document.getElementById("northButton").addEventListener("mousedown", moveViewNorth);
document.getElementById("southButton").addEventListener("mousedown", moveViewSouth);

//Add movement termination event, on mouse up
window.addEventListener("mouseup", stopViewMovement);

//Setup the actual functions for how movement works
function moveViewWest() {currentViewMovementX = 0 - moveSpeed;}
function moveViewEast() {currentViewMovementX = moveSpeed;}
function moveViewNorth() {currentViewMovementY = 0 - moveSpeed;}
function moveViewSouth() {currentViewMovementY = moveSpeed;}

/**
 * Stops all movement of the view.  Used when mouseup is detected.
 */
function stopViewMovement() {
    currentViewMovementX = 0;
    currentViewMovementY = 0;
}

/**
 * If there is a nonzero movement vector, apply it to the viewport.  Run this function every frame.
 */
function updateMovement() {
    viewX += currentViewMovementX;
    viewY += currentViewMovementY;
}

/**
 * update holds all the "every frame do this" things for now.  Later might separate these into
 * many functions, but for now it will hold every update function and thing to do every frame
 */
function update() {
    ctx.clearRect(0,0,map.width,map.height);
    drawSampleHexes(); //DEBUG
    updateMovement();
    scaleCanvasToContainer();
}

function scaleCanvasToContainer() {
    if(map.width != mapContainer.clientWidth || map.height != mapContainer.clientHeight) {
        console.log("mw=" + map.width +" mh=" + map.height +
            " mcw=" + mapContainer.clientWidth + " mch=" + mapContainer.clientHeight);
        map.width = mapContainer.clientWidth;
        map.height = mapContainer.clientHeight;
        //Don't need to do anything to viewport coordinates, it works out on its own somehow.
    }
}

/**
 * DEBUG:  Draws a bunch of various hexagons around the map.
 */
function drawSampleHexes() {
    drawHexFromCubic(0,0,0);
    drawHexFromCubic(1,0,-1);
    drawHexFromCubic(6,-4,-2);
    drawHexFromCubic(-1,-2,3);
    drawHexFromCubic(2,-2,0);
    drawHexFromCubic(1,4,-5);
}

//Set regular screen updates via updateDisplay()
setInterval(update, 1000/60);

/*
 Resources I've been using for stuff.  Will probably come in handy later.

 y = 3/2 * s * b
 b = 2/3 * y / s
 x = sqrt(3) * s * ( b/2 + r)
 x = - sqrt(3) * s * ( b/2 + g )
 r = (sqrt(3)/3 * x - y/3 ) / s
 g = -(sqrt(3)/3 * x + y/3 ) / s

 r + b + g = 0

 http://i.imgur.com/AJoDm.gif
 http://www.redblobgames.com/grids/hexagons/

 Current stackoverflow thread for full tesselation:
 http://stackoverflow.com/questions/33271449/tessellating-hexagons-over-a-rectangle
 */<commit_msg>added hex class, refactored everything around it<commit_after>/**
 * Created by Matthew on 10/21/15.
 */

//Get canvas's element.  Used for drawing the grids and whatnot
map = document.getElementById("map");
var ctx = map.getContext("2d");

mapContainer = document.getElementById("mapContainer");

//length of edge, or radius from center to vertex.
var hexRadius = 30;

//viewport variables.  Default to geographic 0,0 being middle of screen.
var viewX = 0 - map.width/2;
var viewY = 0 - map.height/2;

//movement speed, pixels per frame that the viewport moves when you press the movement buttons.
var moveSpeed = 3;

//When moving (between mousedown and mouseup) store movement vector in these two variables.
var currentViewMovementX = 0;
var currentViewMovementY = 0;

/**
 * This is the hex object constructor.  It takes a cubic coordinate input.
 * @param x
 * @param y
 * @param z
 * @param color
 * @constructor
 */
function Hex(x, y, z, color) {
    //x, y, and z are the cubic coordinates of the hex.
    this.x = x;
    this.y = y;
    this.z = z;

    //Calculate x/y cartesian coordinates, store in coords.
    this.coords = {x: x * 1.5 * hexRadius, y: Math.sqrt(3) * ((x/2) + z) * hexRadius};

    //Background fill color for the tile
    this.color = color;

    /**
     * Draw draws the hex in the canvas, at the correct point in the view given the coordinates.
     */
    this.draw = function() {
        var a = (Math.PI * 2)/6;
        ctx.beginPath();
        ctx.translate(this.coords.x - viewX, this.coords.y - viewY); //offset by the viewport here
        ctx.rotate(0);
        ctx.moveTo(hexRadius,0);
        for (var i = 1; i < 6; i++) {
            ctx.lineTo(hexRadius*Math.cos(a * i), hexRadius*Math.sin(a * i));
        }
        ctx.closePath();
        ctx.fillStyle = this.color;
        ctx.strokeStyle = "#000000";
        ctx.stroke();
        ctx.fill();
        //Undo translation.  Is this the way I want to do it though?  It's a little dirty...
        ctx.translate(0 - (this.coords.x - viewX), 0 - (this.coords.y - viewY)); //reset the context's original position
    }
}

var HEXES = [];

//Setup event listeners for the movement buttons
document.getElementById("westButton").addEventListener("mousedown", moveViewWest);
document.getElementById("eastButton").addEventListener("mousedown", moveViewEast);
document.getElementById("northButton").addEventListener("mousedown", moveViewNorth);
document.getElementById("southButton").addEventListener("mousedown", moveViewSouth);

//Add movement termination event, on mouse up
window.addEventListener("mouseup", stopViewMovement);

//Setup the actual functions for how movement works
function moveViewWest() {currentViewMovementX = 0 - moveSpeed;}
function moveViewEast() {currentViewMovementX = moveSpeed;}
function moveViewNorth() {currentViewMovementY = 0 - moveSpeed;}
function moveViewSouth() {currentViewMovementY = moveSpeed;}

//Stops all movement of the view.  Used when mouseup is detected.
function stopViewMovement() { currentViewMovementX = 0; currentViewMovementY = 0; }

//If there is a nonzero movement vector, apply it to the viewport.  Run this function every frame.
function updateMovement() { viewX += currentViewMovementX; viewY += currentViewMovementY; }

/**
 * update holds all the "every frame do this" things for now.  Later might separate these into
 * many functions, but for now it will hold every update function and thing to do every frame
 */
function update() {
    ctx.clearRect(0,0,map.width,map.height);
    drawHexes();
    updateMovement();
    scaleCanvasToContainer();
}

/**
 * Main draw function.  For now, draws all the hexagons in the HEXES array.  Later, will check which hexes are
 * in the current view, and will specifically draw them.  TODO: Tessellate hexes, including blank hexes, across view.
 */
function drawHexes() {
    for(var i = 0; i < HEXES.length; i++) {
        HEXES[i].draw();
    }
}

/**
 * If the window has changed size, scale the canvas to fit it.
 */
function scaleCanvasToContainer() {
    if(map.width != mapContainer.clientWidth || map.height != mapContainer.clientHeight) {
        map.width = mapContainer.clientWidth;
        map.height = mapContainer.clientHeight;
    }
}

//SETUP SOME TEST HEXES TO MAKE SURE EVERYTHING DISPLAYS CORRECTLY.  TODO:  REMOVE THIS BECAUSE IT'S FOR DEBUGGING
HEXES.push(new Hex(0,0,0,"#B8E68A"));
HEXES.push(new Hex(1,-1,0,"#FFFF99"));
HEXES.push(new Hex(-1,1,0,"#B8E68A"));
HEXES.push(new Hex(0,-1,1,"#B8E68A"));
HEXES.push(new Hex(0,1,-1,"#75D1FF"));
HEXES.push(new Hex(1,0,-1,"#75D1FF"));
HEXES.push(new Hex(-1,0,1,"#75D1FF"));
HEXES.push(new Hex(2,-1,-1,"#FFFF99"));

//Set regular screen updates via updateDisplay()
setInterval(update, 1000/60);

/*
 Resources I've been using for stuff.  Will probably come in handy later.

 y = 3/2 * s * b
 b = 2/3 * y / s
 x = sqrt(3) * s * ( b/2 + r)
 x = - sqrt(3) * s * ( b/2 + g )
 r = (sqrt(3)/3 * x - y/3 ) / s
 g = -(sqrt(3)/3 * x + y/3 ) / s

 r + b + g = 0

 http://i.imgur.com/AJoDm.gif
 http://www.redblobgames.com/grids/hexagons/

 Current stackoverflow thread for full tesselation:
 http://stackoverflow.com/questions/33271449/tessellating-hexagons-over-a-rectangle
 */<|endoftext|>


<commit_before>(function() {
/**
 * The MIT License
 *
 * Copyright (c) 2011 Stan Angeloff
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
var VERSION = '0.2';
/* TODO:
var $first     = null,
    $visible   = null,
    $highlight = [],
    $cache     = {},
    $previous  = null,
    hash       = null,
    sequence   = 0;
$.mobile || ($.mobile = {});
$.mobile.scrollTop = function uiScrollTop(offset) {
  if (typeof (offset) === 'undefined') {
    return (document.body.scrollTop || document.documentElement.scrollTop || window.pageYOffset);
  } else {
    document.body.scrollTop =
    document.documentElement.scrollTop =
    window.pageYOffset = offset;
    return $.mobile;
  }
};
$.mobile.loading = function uiLoading(flag) {
  var $loading;
  $('#loading').remove();
  if (flag) {
    $loading = $(document.createElement('div')).attr({ id: 'loading' }).addClass('ui-loading');
    $loading.append($(document.createElement('div')).addClass('ui-spinner'));
    $(document.body).append($loading);
    $(window).trigger('resize');
  }
  return $.mobile;
};
$.mobile.showPage = function uiShowPage(page, options) {
  var id = '#' + page.attr('id');
  options || (options = {});
  if ($previous && $previous.attr('id') !== page.attr('id')) {
    $previous.trigger('pagebeforehide').trigger('pagehide');
  }
  if (options.type === 'dialog') {
    $previous = $visible;
    if ($visible) {
      $visible.data('mobile:scrollTop', $.mobile.scrollTop()).addClass('collapsed');
    }
  } else {
    $previous = null;
    if ($visible) {
      $visible.data('mobile:scrollTop', $.mobile.scrollTop()).trigger('pagebeforehide').addClass('collapsed').trigger('pagehide');
    }
  }
  if ($highlight.length) {
    $highlight.removeClass('highlight');
  }
  $visible = page.trigger('pagebeforeshow').removeClass('collapsed').trigger('pageshow');
  $visible.reflow();
  $highlight = $visible.find('[href="' + id + '"]').addClass('highlight');
  options.event && options.event.stopPropagation();
  setTimeout(function() {
    $.mobile.scrollTop($visible.data('mobile:scrollTop') || 0);
    $.mobile.updateLayout();
  }, 25);
  page.get(0).focus();
  return $.mobile;
};
$.mobile.changePage = function uiChangePage(options) {
  var page = null, found = false;
  options || (options = {});
  if (typeof (options.target) === 'string') {
    var $pages = $('[data-role="page"]');
    options.target = options.target.replace(/^#/, '');
    for (var i = 0; i < $pages.length; i ++) {
      var $this = $($pages.get(i));
      if ($this.attr('id') === options.target) {
        found = true;
        if ($this.hasClass('collapsed')) {
          page = $this;
          hash = options.target;
          break;
        }
      }
    }
    if ( ! found) {
      if ($cache[options.target] && options.method !== 'POST') {
        $.mobile.changePage($.extend(options, { target: $cache[options.target] }));
      } else {
        $.mobile.loading(true);
        $.ajax({
          dataType: 'html',
          data:     options.data,
          type:     (options.method && options.method.toUpperCase()) || 'GET',
          cache:    false,
          url:      options.target,
          success:  function(html, code, xhr) {
            var $result = $(document.createElement('div')).html(html),
                $page   = $result.find('[data-role="page"]'),
                target  = options.target, id;
            if ( ! $page.length) {
              $page = $(document.createElement('div'));
            }
            if ($cache[target]) {
              $cache[target].remove();
              delete $cache[target];
            }
            id = $page.attr('id');
            if (id && $('#' + id).length) {
              $page.attr({ id: 'page-' + (++ sequence) });
            }
            $page.page();
            if ($page.data('cache') === 'true') {
              $cache[target] = $page;
            } else {
              $page.bind('pagehide', function() {
                $page.remove();
              });
            }
            $.mobile.initialize($page);
            $(document.body).append($page);
            $.mobile.loading(false);
            $.mobile.changePage($.extend(options, { target: $page }));
            if (options.type !== 'dialog') {
              hash = target;
              location.hash = hash;
            }
          },
          error: function(xhr, code, exception) {
            $.mobile.loading(false);
            alert('Whoops! We failed to load the requested page from the server. Please make sure you are connected to the internet and try again.\n\n[' + xhr.status + '] ' + (exception || xhr.statusText));
          }
        });
      }
    }
  } else {
    ($visible === options.target) || (page = options.target);
  }
  if (page) {
    $.mobile.showPage(page, options);
  } else {
    options.event && options.event.preventDefault();
  }
  return $.mobile;
};
$.mobile.closeDialog = function uiCloseDialog() {
  $.mobile.changePage({
    target: $previous
  });
}
$.mobile.serialize = function serialize(form) {
  var data = {};
  for (var i = 0; i < form.elements.length; i ++) {
    var element = form.elements[i];
    if (element.name && ! element.disabled) {
      if (element.tagName === 'SELECT') {
        data[element.name] = element.options[element.selectedIndex].value;
      } else if (element.tagName === 'INPUT') {
        if (element.type === 'checkbox' || element.type === 'radio') {
          if (element.checked) {
            data[element.name] = (element.value || 'on');
          }
        } else {
          data[element.name] = (element.value || '').replace(/\r\n|\r|\n/g, '\n');
        }
      } else if (element.tagName === 'TEXTAREA') {
        data[element.name] = (element.value || '').replace(/\r\n|\r|\n/g, '\n');
      }
    }
  }
  return data;
};
$.fn.addTheme = function uiAddTheme(theme, inherit, propagate) {
  inherit   && (inherit   = this.parent('[data-theme]').data('theme'));
  propagate && (propagate = this.find('[data-role]:not([data-theme])'));
  for (var i = 0; i < this.length; i ++) {
    var element = $(this.get(i));
    if ( ! (element.attr('class') || '').match(/\btheme-\w/)) {
      var local = (element.data('theme') || inherit || theme);
      element.addClass('theme-' + local);
      propagate && propagate.each(function() {
        $(this).addClass('theme-' + local);
      });
    }
  }
  return this;
};
$.fn.addIcon = function uiAddIcon(icon) {
  for (var i = 0; i < this.length; i ++) {
    var element = $(this.get(i));
    if ( ! (element.attr('class') || '').match(/\bicon-\w+/)) {
      var local = (element.data('icon') || icon);
      if (local) {
        element.addClass('ui-icon ui-icon-' + local);
      }
    }
  }
  return this;
};
$.fn.reflow = function uiReflow() {
  this.find('[data-role="content"]').content();
  return this;
};
$.fn.none = function uiNone() {};
$.fn.page = function uiPage() {
  if (this.hasClass('ui-page')) {
    return this;
  }
  this.addClass('ui-page');
  this.delegate('a', 'click', function(event) {
    var $this = $(this);
    if ($this.data('ajax') === 'false' || $this.data('role') === 'none' || $this.attr('rel') === 'external') {
      return false;
    }
    if ($this.hasClass('ui-disabled')) {
      event.preventDefault();
      event.stopPropagation();
      return false;
    }
    this.blur();
    var type = $this.data('rel'),
        href = $this.attr('href');
    if (href.indexOf('mailto:') === 0 || href.indexOf('sms:') === 0) {
      return false;
    }
    if (type === 'back') {
      if ($previous) {
        $.mobile.closeDialog();
      } else {
        history.back();
      }
      event.preventDefault();
    } else {
      $.mobile.changePage({
        event:  event,
        target: $this.attr('href'),
        type:   type
      });
    }
  });
  this.delegate('form', 'submit', function(event) {
    var $this = $(this);
    if ($this.data('ajax') === 'false' || $this.data('role') === 'none') {
      return false;
    }
    var type = $this.data('rel');
    $.mobile.changePage({
      event:  event,
      target: $this.attr('action'),
      method: $this.attr('method'),
      data:   $.mobile.serialize(this),
      type:   type
    });
  });
  if ( ! $first) {
    $first = this;
  }
  this.addClass('collapsed');
  return this;
};
var $fixed = [];
$.fn.header = function uiHeader() {
  this.addClass('ui-header').addTheme('a', false, true);
  if (this.data('position') === 'fixed') {
    this.addClass('ui-fixed');
    $fixed.push([this, this.parent('[data-role="page"]'), 'top']);
  }
  return this;
};
$.fn.navbar = function uiNavbar() {
  this.addClass('ui-navbar').addTheme('a', true).find('a').addIcon();
  var $children = this.find('li')
  $children.css({ width: (100 / $children.length) + '%' });
  return this;
};
$.fn.content = function uiContent() {
  this.addClass('ui-content').addTheme('c');
  var top = 0, bottom = 0;
  this.prev('[data-role="header"]').each(function() {
    var $this = $(this);
    if ($this.css('position') === 'absolute') {
      top = top + $this.height();
    }
  });
  this.next('[data-role="footer"]').each(function() {
    var $this = $(this);
    if ($this.css('position') === 'absolute') {
      bottom = bottom + $this.height();
    }
  });
  this.css({
    'padding-top':    top    + 'px',
    'padding-bottom': bottom + 'px'
  });
  return this;
};
$.fn.fieldcontain = function uiFieldContain() {
  this.addClass('ui-field-contain');
  return this;
};
$.fn.listview = function uiListView() {
  var buttons = this.find('[data-role="button"]'),
      split   = this.data('split-icon'),
      icon    = this.data('icon');
  this.addClass('ui-list-view').addClass('align-' + (this.data('iconpos') || 'right')).addTheme('d', true, false);
  if (this.data('inset') === 'true') {
    this.addClass('ui-inset');
  }
  buttons.addTheme('d', false, false);
  if (split) {
    buttons.addIcon(split);
  }
  this.find('li').each(function() {
    var $this = $(this), children = $this.children();
    $this.addIcon(icon);
    if (children.length && children[0].tagName === 'A') {
      $(children[0]).addClass('ui-a-block');
      $this.addClass('ui-li-block');
    }
  });
  return this;
};
$.fn['list-divider'] = function uiListDivider() {
  this.addClass('ui-list-divider').addTheme('b', false);
  return this;
};
$.fn.controlgroup = function uiControlGroup() {
  var $children, type = this.data('type');
  this.addClass('ui-control-group').addClass('orientation-' + (type || 'horizontal')).addTheme('d', true, true);
  if (type !== 'vertical') {
    $children = this.children();
    $children.css({ width: (100 / $children.length) + '%' });
  }
  $children = this.find('input');
  if ($children.length) {
    var self = this;
    this.delegate('input', 'change', function() {
      $children.each(function() {
        $(this).closest('[data-role="button"]')[this.checked ? 'addClass' : 'removeClass']('highlight');
      });
    });
    $children.first().trigger('change');
  }
  return this;
};
$.fn.button = function uiButton() {
  this.addClass('ui-button').addTheme('b', true, false).addIcon().addClass('icon-' + (this.data('iconpos') || 'left'));
  if (this.data('icon-only') === 'true') {
    this.addClass('ui-icon-only');
  }
  if (this.data('inline') === 'true') {
    this.addClass('ui-inline');
  }
  return this;
};
$.fn.collapsible = function uiCollapsible() {
  this.addClass('ui-collapsible');
  if (this.data('collapsed') === 'true') {
    this.addClass('closed');
  }
  var self = this;
  this.children().first().addClass('ui-toggle').bind('click', function() {
    self.toggleClass('closed');
  });
};
$.fn.footer = function uiFooter() {
  this.addClass('ui-footer').addTheme('c', false, true);
  if (this.data('position') === 'fixed') {
    this.addClass('ui-fixed').css({ top: (window.innerHeight - this.height()) + 'px' });
    $fixed.push([this, this.parent('[data-role="page"]'), 'bottom']);
  } else {
    $fixed.push([this, this.parent('[data-role="page"]'), 'bottom-if-needed']);
  }
  return this;
};
var hidden = false;
$(window).bind('touchmove', function onScrollStart(event) {
  if ( ! hidden) {
    hidden = true;
    for (var i = 0; i < $fixed.length; i ++) {
      var config = $fixed[i];
      if (config[1].hasClass('collapsed')) {
        continue;
      }
      if (config[2] === 'top') {
        config[0].css({ top: '0px' });
      } else if (config[2] === 'bottom-if-needed') {
        if (document.body.scrollHeight > window.innerHeight) {
          config[0].css({ position: 'relative', bottom: 'auto' });
        }
      } else {
        config[0].css({ top: (document.body.scrollHeight - config[0].height()) + 'px' });
      }
    }
  }
});
$(window).bind('scroll resize orientationchange', $.mobile.updateLayout = function uiUpdateLayout() {
  var top = $.mobile.scrollTop(), reflow = false;
  for (var i = 0; i < $fixed.length; i ++) {
    var config = $fixed[i];
    if (config[1].hasClass('collapsed')) {
      continue;
    }
    if (config[2] === 'top') {
      config[0].css({ top: top + 'px' });
    } else if (config[2] === 'bottom-if-needed') {
      if (document.body.scrollHeight - 5 <= window.innerHeight) {
        config[0].css({ position: 'absolute', bottom: '0px' });
      } else {
        config[0].css({ position: 'relative', bottom: 'auto' });
      }
      reflow = true;
    } else {
      config[0].css({ top: (top + window.innerHeight - config[0].height()) + 'px' });
    }
  }
  hidden = false;
  if (reflow) {
    $visible.reflow();
  }
  $('#loading').css({
    width:  window.innerWidth          + 'px',
    height: document.body.scrollHeight + 'px'
  }).children('.ui-spinner').css({
    top:  ($.mobile.scrollTop() + Math.round(window.innerHeight / 2)) + 'px',
    left: Math.round(window.innerWidth  / 2) + 'px'
  });
});
$(window).bind('touchend', function onScrollEnd() {
  if (hidden) {
    $.mobile.updateLayout();
  }
});
var initialized = false;
$.mobile.autoInitialize = true;
$.mobile.initialize = function uiInitialize(scope) {
  ((scope && scope.find('[data-role]')) || $('[data-role]')).each(function() {
    var $this = $(this);
    $this[$this.data('role')]();
  });
  var buttonsQuery = 'input[type="button"], input[type="submit"], button';
  ((scope && scope.find(buttonsQuery)) || $(buttonsQuery)).each(function() {
    $(this).button();
  });
  if ( ! initialized) {
    initialized = true;
    $(window).bind('hashchange', function(event) {
      var value = location.hash.replace(/^#/, '');
      if (hash !== value) {
        hash = value;
        $.mobile.changePage({
          event:  event,
          target: hash || $first
        });
      }
    });
    if ( ! ('onhashchange' in window)) {
      var last = location.hash;
      setInterval(function() {
        if (location.hash !== last) {
          last = location.hash;
          $(window).trigger('hashchange', [null, last]);
        }
      }, 50);
    }
    $(window).trigger('hashchange', [null, null]);
    window.scrollTo(0, 0);
  }
};
$(document).ready(function() {
  if ($.mobile.autoInitialize) {
    $.mobile.initialize();
  }
});
*/
}).call($.mobile = {});
<commit_msg>Step 1: restore and refactor part of the original code, no styles yet and not much functionality.<commit_after>(function(window, document, location) {
/**
 * The MIT License
 *
 * Copyright (c) 2011 Stan Angeloff
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
var VERSION = '0.2';
var self = this;
var $topPage;
var $visiblePage;
var $hiddenPage;
var $cachedPages = {};
var pageSequence = 0;
var pageHash;
var $fixedWidgets = [];
var SCROLLTOP = 'zoey:scroll-top';
function scrollTop(offset) {
  if (arguments.length) {
    document.body.scrollTop = document.documentElement.scrollTop = window.pageYOffset = offset;
  } else {
    return (document.body.scrollTop || document.documentElement.scrollTop || window.pageYOffset);
  }
};
self.showPage = function($page, options) {
  if ($visiblePage && $visiblePage.attr('id') === $page.attr('id')) {
    return self;
  }
  options || (options = {});
  ($hiddenPage && $hiddenPage.attr('id') !== $page.attr('id')) && $hiddenPage.trigger('pagebeforehide').trigger('pagehide');
  if (options.type === 'dialog') {
    $hiddenPage = $visiblePage;
    $visiblePage && $visiblePage.data(SCROLLTOP, scrollTop()).addClass('collapsed');
  } else {
    $hiddenPage = null;
    $visiblePage && $visiblePage.data(SCROLLTOP, scrollTop()).trigger('pagebeforehide').addClass('collapsed').trigger('pagehide');
  }
  $visiblePage = $page.trigger('pagebeforeshow').removeClass('collapsed').trigger('pageshow');
  setTimeout(function() {
    scrollTop($visiblePage.data(SCROLLTOP) || 0);
  }, 25);
  options.event && options.event.stopPropagation();
  $page.get(0).focus();
  return self;
};
self.changePage = function(options) {
  var $page;
  options || (options = {});
  if (typeof (options.target) === 'string') {
    options.target = options.target.replace(/^#/, '');
    $('[data-role="page"]').each(function() {
      if ($(this).attr('id') === options.target) {
        $page = $(this);
        pageHash = options.target;
      }
    });
    if ( ! $page) {
    }
  } else {
    $page = options.target;
  }
  if ($page) {
    self.showPage($page, options);
  } else {
    options.event && options.event.preventDefault();
  }
  return self;
};
self.widgets = {
  page: function() {
    this.delegate('a', 'click', function(event) {
    }).delegate('form', 'submit', function(event) {
    });
    $topPage || ($topPage = this);
    this.addClass('collapsed');
  },
  navigation: function() {
    var $children = this.children();
    $children.css({ width: (100 / $children.length) + '%' });
  }
};
self.role = function(role) {
  var $control = this;
  var widget = self.widgets[role];
  $control.hasClass('ui-' + role) || (widget && widget.call($control.addClass('ui-' + role)));
  ['theme', 'icon'].forEach(function(type) {
    $control.data(type) && $control.addClass('ui-has-' + type + ' ui-' + type + '-' + $control.data(type));
  });
};
self.initialize = function(scope, options) {
  options || (options = {});
  (scope || $('html')).find('[data-role], input[type="button"], input[type="submit"], button').each(function() {
    self.role.apply($(this), [$(this).data('role') || 'button']);
  });
  if ( ! scope) {
    $('html').addClass('ui-theme-' + (options.theme || 'c'));
    $(window).bind('hashchange', function(event) {
      var value = location.hash.replace(/^#/, '');
      if (pageHash !== value) {
        pageHash = value;
        self.changePage({
          event:  event,
          target: pageHash || $topPage
        });
      }
    }).trigger('hashchange');
    if ( ! window.onhashchange) {
      var last = location.hash;
      setInterval(function() {
        if (last !== location.hash) {
          $(window).trigger('hashchange');
          last = location.hash;
        }
      }, 50);
    }
    window.scrollTo(0, 0);
  }
};
$(document).ready(function() {
  self.preventInitialize || self.initialize();
});
}).apply(this.Zoey = {}, [window, document, location]);
<|endoftext|>


<commit_before>define(["dojo/_base/declare", "./Selection", "dojo/on", "put-selector/put", "dojo/has"], function(declare, Selection, listen, put, has){
return declare(Selection, {
	// summary:
	//		Add cell level selection capabilities to a grid. The grid will have a selection property and
	//		fire "dgrid-select" and "dgrid-deselect" events.

	// ensure we don't select when an individual cell is not identifiable
	selectionDelegate: ".dgrid-cell",

	select: function(cell, toCell, value){
		var i, id;
		if(value === undefined){
			// default to true
			value = true;
		}
		if(typeof cell != "object" || !("element" in cell)){
			cell = this.cell(cell);
		}else if(!cell.row){
			// it is row, with the value being a hash
			for(id in value){
				this.select(this.cell(cell.id, id), null, value[id]);
			}
			return;
		}
		if(this.allowSelect(cell)){
			var selection = this.selection,
				rowId = cell.row.id,
				previousRow = selection[rowId];
			if(!cell.column){
				for(i in this.columns){
					this.select(this.cell(rowId, i), null, value);
				}
				return;
			}
			var previous = previousRow && previousRow[cell.column.id];
			if(value === null){
				// indicates a toggle
				value = !previous;
			}
			var element = cell.element;
			previousRow = previousRow || {};
			previousRow[cell.column.id] = value;
			this.selection[rowId] = previousRow;

			// Check for all-false objects to see if it can be deleted.
			// This prevents build-up of unnecessary iterations later.
			var hasSelected = false;
			for(i in previousRow){
				if(previousRow[i] === true){
					hasSelected = true;
					break;
				}
			}
			if(!hasSelected){ delete this.selection[rowId]; }

			if(element){
				// add or remove classes as appropriate
				if(value){
					put(element, ".dgrid-selected.ui-state-active");
				}else{
					put(element, "!dgrid-selected!ui-state-active");
				}
			}
			if(value != previous && element){
				this._selectionEventQueue(value, "cells").push(cell);
			}
			if(toCell){
				// a range
				if(!toCell.element){
					toCell = this.cell(toCell);
				}
				var toElement = toCell.element;
				var fromElement = cell.element;
				// find if it is earlier or later in the DOM
				var traverser = (toElement && (toElement.compareDocumentPosition ?
					toElement.compareDocumentPosition(fromElement) == 2 :
					toElement.sourceIndex > fromElement.sourceIndex)) ? "nextSibling" : "previousSibling";
				// now we determine which columns are in the range
				var idFrom = cell.column.id, idTo = toCell.column.id, started, columnIds = [];
				for(id in this.columns){
					if(started){
						columnIds.push(id);
					}
					if(id == idFrom && (idFrom = columnIds) || // once found, we mark it off so we don't hit it again
						id == idTo && (idTo = columnIds)){
						columnIds.push(id);
						if(started || // last id, we are done
							(idFrom == columnIds && id == idTo)){ // the ids are the same, we are done
							break;
						}
						started = true;
					}
				}
				// now we iterate over rows
				var row = cell.row, nextNode = row.element;
				toElement = toCell.row.element;
				do{
					// looping through each row..
					// and now loop through each column to be selected
					for(i = 0; i < columnIds.length; i++){
						cell = this.cell(nextNode, columnIds[i]);
						this.select(cell, null, value);
					}
					if(nextNode == toElement){
						break;
					}
				}while((nextNode = cell.row.element[traverser]));
			}
		}
	},
	isSelected: function(object, columnId){
		// summary:
		//		Returns true if the indicated cell is selected.

		if(typeof object === "undefined" || object === null){
			return false;
		}
		if(!object.element){
			object = this.cell(object, columnId);
		}

		// First check whether the given cell is indicated in the selection hash;
		// failing that, check if allSelected is true (testing against the
		// allowSelect method if possible)
		var rowId = object.row.id;
		if(rowId in this.selection){
			return !!this.selection[rowId][object.column.id];
		}else{
			return this.allSelected && (!object.row.data || this.allowSelect(object));
		}
	},
	clearSelection: function(exceptId){
		// disable exceptId in cell selection, since it would require double parameters
		exceptId = false;
		this.inherited(arguments);
	}
});
});
<commit_msg>CellSelection: support (de)selecting all columns in row if row object is passed<commit_after>define([
	"dojo/_base/declare",
	"dojo/aspect",
	"dojo/on",
	"dojo/has",
	"./Selection",
	"put-selector/put"
], function(declare, aspect, listen, has, Selection, put){

return declare(Selection, {
	// summary:
	//		Add cell level selection capabilities to a grid. The grid will have a selection property and
	//		fire "dgrid-select" and "dgrid-deselect" events.

	// ensure we don't select when an individual cell is not identifiable
	selectionDelegate: ".dgrid-cell",

	select: function(cell, toCell, value){
		var i, id;
		if(value === undefined){
			// default to true
			value = true;
		}
		if(typeof cell != "object" || !("element" in cell)){
			cell = this.cell(cell);
		}else if(!cell.row){
			// Row object was passed instead of cell
			if(value && typeof value === "object"){
				// value is a hash of true/false values
				for(id in value){
					this.select(this.cell(cell.id, id), null, value[id]);
				}
			}else{
				// Select/deselect all columns in row
				for(id in this.columns){
					this.select(this.cell(cell.id, id), null, value);
				}
			}
			return;
		}
		if(this.allowSelect(cell)){
			var selection = this.selection,
				rowId = cell.row.id,
				previousRow = selection[rowId];
			if(!cell.column){
				for(i in this.columns){
					this.select(this.cell(rowId, i), null, value);
				}
				return;
			}
			var previous = previousRow && previousRow[cell.column.id];
			if(value === null){
				// indicates a toggle
				value = !previous;
			}
			var element = cell.element;
			previousRow = previousRow || {};
			previousRow[cell.column.id] = value;
			this.selection[rowId] = previousRow;

			// Check for all-false objects to see if it can be deleted.
			// This prevents build-up of unnecessary iterations later.
			var hasSelected = false;
			for(i in previousRow){
				if(previousRow[i] === true){
					hasSelected = true;
					break;
				}
			}
			if(!hasSelected){ delete this.selection[rowId]; }

			if(element){
				// add or remove classes as appropriate
				if(value){
					put(element, ".dgrid-selected.ui-state-active");
				}else{
					put(element, "!dgrid-selected!ui-state-active");
				}
			}
			if(value != previous && element){
				this._selectionEventQueue(value, "cells").push(cell);
			}
			if(toCell){
				// a range
				if(!toCell.element){
					toCell = this.cell(toCell);
				}
				var toElement = toCell.element;
				var fromElement = cell.element;
				// find if it is earlier or later in the DOM
				var traverser = (toElement && (toElement.compareDocumentPosition ?
					toElement.compareDocumentPosition(fromElement) == 2 :
					toElement.sourceIndex > fromElement.sourceIndex)) ? "nextSibling" : "previousSibling";
				// now we determine which columns are in the range
				var idFrom = cell.column.id, idTo = toCell.column.id, started, columnIds = [];
				for(id in this.columns){
					if(started){
						columnIds.push(id);
					}
					if(id == idFrom && (idFrom = columnIds) || // once found, we mark it off so we don't hit it again
						id == idTo && (idTo = columnIds)){
						columnIds.push(id);
						if(started || // last id, we are done
							(idFrom == columnIds && id == idTo)){ // the ids are the same, we are done
							break;
						}
						started = true;
					}
				}
				// now we iterate over rows
				var row = cell.row, nextNode = row.element;
				toElement = toCell.row.element;
				do{
					// looping through each row..
					// and now loop through each column to be selected
					for(i = 0; i < columnIds.length; i++){
						cell = this.cell(nextNode, columnIds[i]);
						this.select(cell, null, value);
					}
					if(nextNode == toElement){
						break;
					}
				}while((nextNode = cell.row.element[traverser]));
			}
		}
	},
	isSelected: function(object, columnId){
		// summary:
		//		Returns true if the indicated cell is selected.

		if(typeof object === "undefined" || object === null){
			return false;
		}
		if(!object.element){
			object = this.cell(object, columnId);
		}

		// First check whether the given cell is indicated in the selection hash;
		// failing that, check if allSelected is true (testing against the
		// allowSelect method if possible)
		var rowId = object.row.id;
		if(rowId in this.selection){
			return !!this.selection[rowId][object.column.id];
		}else{
			return this.allSelected && (!object.row.data || this.allowSelect(object));
		}
	},
	clearSelection: function(exceptId){
		// disable exceptId in cell selection, since it would require double parameters
		exceptId = false;
		this.inherited(arguments);
	}
});
});
<|endoftext|>


<commit_before>import React from 'react';
import { render } from 'react-dom';
import { BrowserRouter as Router } from 'react-router-dom';
import { Provider } from 'react-redux'
import { createStore } from 'redux'
import App from './components/app/app.js';
import reducers from './reducers'

const initialState = {
  loading: false
};

const store = createStore(reducers, initialState)
store.subscribe(() => console.log(store.getState()))
render(
  <Provider store={store}>
    <Router>
      <App/>
    </Router>
  </Provider>,
  document.getElementById('app')
);
<commit_msg>Comment store state log<commit_after>import React from 'react';
import { render } from 'react-dom';
import { BrowserRouter as Router } from 'react-router-dom';
import { Provider } from 'react-redux'
import { createStore } from 'redux'
import App from './components/app/app.js';
import reducers from './reducers'

const store = createStore(reducers)

// store.subscribe(() => console.log(store.getState()))

render(
  <Provider store={store}>
    <Router>
      <App/>
    </Router>
  </Provider>,
  document.getElementById('app')
);
<|endoftext|>