<commit_before>const INLINE_SVG_REGEX =
    /url\((\\?("|"|'))(data:image\/svg\+xml;utf8,.(((?!\1).)|\\\1)*)\1\)/g;

const EXTERNAL_PNG_REGEX =
    /url\((\\?("|"|'))(.*\.png)\1\)/g;

// Converts map nodes (the entire map or individual tiles) to images.
// Basic mechanism:
//
// 1. Maintain a string with the current style, taken from grid.css and any
//    theme-loaded styles.
// 2. Convert the node to XML string.
// 3. Place the XML string and the maintained style string inside a
//    foreignObject string.
// 4. Place the foreignObject string inside a new SVG string.
// 5. Create an image and set its src to a data URL version of that SVG.
// 6. Draw the image to a canvas, scaling it in the process.
// 7. Use the canvas's toDataUrl() to obtain a PNG data URL.
//
// This is inspired by https://github.com/tsayen/dom-to-image and is similar in
// principle, except that I can get away with not cloning any nodes by
// maintaining the style string and carefully escaping embedded SVGs.
class GridImager {
  constructor(options) {
    this.selectorsOfElementsToStrip_ = options.selectorsOfElementsToStrip || [];
    this.scale_ = options.scale || 1;
    this.disableSmoothing_ = options.disableSmoothing || false;
    this.margins_ = options.margins || 0;
    this.xmlPreProcessor_ = options.xmlPreProcessor || null;
    this.cropLeft_ = options.cropLeft || 0;
    this.cropRight_ = options.cropRight || 0;
    this.cropTop_ = options.cropTop || 0;
    this.cropBottom_ = options.cropBottom || 0;

    this.cssFiles_ = [];   // Order matters!
    this.styleString_ = '';
    this.imageElementContainer_ = null;
  }

  createImageElementContainer_() {
    if (this.imageElementContainer_) return;
    const parent = document.getElementById('mapContainer');
    this.imageElementContainer_ =
        createAndAppendDivWithClass(parent, 'grid-imager-image-container');
  }

  clone(options) {
    const gridImager = new GridImager({
      selectorsOfElementsToStrip:
        options.selectorsOfElementsToStrip || this.selectorsOfElementsToStrip_,
      scale: options.scale || this.scale_,
      disableSmoothing:
          options.disableSmoothing !== undefined ? options.disableSmoothing :
            this.disableSmoothing_,
      margins: options.margins != null ? options.margins : this.margins_,
      xmlPreProcessor: options.xmlPreProcessor || this.xmlPreProcessor_,
      cropLeft: options.cropLeft != null ? options.cropLeft : this.cropLeft_,
      cropRight:
          options.cropRight != null ? options.cropRight : this.cropRight_,
      cropTop: options.cropTop != null ? options.cropTop : this.cropTop_,
      cropBottom:
          options.cropBottom != null ? options.cropBottom : this.cropBottom_,
    });
    gridImager.cssFiles_ = this.cssFiles_;
    gridImager.styleString_ = this.styleString_;
    gridImager.imageElementContainer_ = this.imageElementContainer_;
    return gridImager;
  }

  async addCssStyleSheet(index, cssStyleSheet) {
    let cssStr = '';
    const path = cssStyleSheet.href;
    const pathFolder = path.substring(0, path.lastIndexOf('/') + 1);
    for (const rule of cssStyleSheet.cssRules) {
      const selector = rule.selectorText;
      const properties = {};
      for (let i = 0; i < rule.style.length; i++) {
        const propertyName = rule.style.item(i);
        let propertyValue = rule.style.getPropertyValue(propertyName)
            .replace(INLINE_SVG_REGEX, (match, quote, unused, value) =>
              'url("data:image/svg+xml;utf8,' +
                  encodeURIComponent(
                      value.substr(24)
                          .replace(/\\"/g, "'").replace(/%23/g, '#')) + '")');
        propertyValue =
            await this.internExternalImages_(pathFolder, propertyValue);
        properties[propertyName] = propertyValue;
        continue;
      }
      cssStr += `${selector}{`;
      for (const key of Object.keys(properties)) {
        cssStr += `${key}:${properties[key]};`;
      }
      cssStr += '}';
    }
    this.cssFiles_[index] = {path, content: cssStr};
  }

  internExternalImages_(pathFolder, value) {
    this.createImageElementContainer_();
    return new Promise(resolve => {
      const match = EXTERNAL_PNG_REGEX.exec(value);
      if (!match) {
        resolve(value);
        return;
      }
      const src = pathFolder + match[3];
      this.url2imageElement_(src).then(imageElement => {
        imageElement.addEventListener('load', () => {
          const width = imageElement.naturalWidth;
          const height = imageElement.naturalHeight;
          this.imageElement2canvas_(imageElement, width, height)
              .then(canvas => {
                this.imageElementContainer_.removeChild(imageElement);
                this.canvas2dataUrl_(canvas).then(dataUrl => {
                  resolve('url("' + dataUrl + '")');
                });
              });
        });
        this.imageElementContainer_.appendChild(imageElement);
      });
    });
  }

  removeCssFile(path) {
    const indexOfCssFileToRemove =
        this.cssFiles_.findIndex(cssFile => cssFile.path.endsWith(path));
    if (indexOfCssFileToRemove >= 1) {
      this.cssFiles_.splice(indexOfCssFileToRemove, 1);
    }
  }

  recalculateStyleString() {
    this.styleString_ = this.cssFiles_
        .map(cssFile => `<style>${cssFile.content}</style>`)
        .join('\n')
        .replace(/\s+/g, ' ');
  }

  async node2svgElement(node, width, height) {
    width += 2 * this.margins_;
    height += 2 * this.margins_;
    const xml = await this.node2xml_(node, width, height);
    const foreignObjectString = await this.xml2foreignObjectString_(xml, false);
    return await this.foreignObjectString2svgElement_(foreignObjectString);
  }

  async node2svgDataUrl(node, width, height) {
    width += 2 * this.margins_;
    height += 2 * this.margins_;
    const xml = await this.node2xml_(node, width, height);
    const foreignObjectString = await this.xml2foreignObjectString_(xml, true);
    return await this.foreignObjectString2svgDataUrl_(
        foreignObjectString, width, height);
  }

  async node2pngDataUrl(node, width, height) {
    width += 2 * this.margins_;
    height += 2 * this.margins_;
    const xml = await this.node2xml_(node, width, height);
    const foreignObjectString = await this.xml2foreignObjectString_(xml, true);
    const imageElement = await this.foreignObjectString2imageElement_(
        foreignObjectString, width, height);
    const canvas = await this.imageElement2canvas_(imageElement, width, height);
    return await this.canvas2dataUrl_(canvas);
  }

  async node2blob(node, width, height) {
    const pngDataUrl = await this.node2pngDataUrl(node, width, height);
    const binaryString = atob(pngDataUrl.split(',')[1]);
    const length = binaryString.length;
    const binaryArray = new Uint8Array(length);
    for (let i = 0; i < length; i++) {
      binaryArray[i] = binaryString.charCodeAt(i);
    }
    return new Blob([binaryArray], {type: 'image/png'});
  }

  printTimeSince_(name, start, threshold) {
    const duration = Math.round(performance.now() - start);
    if (duration < threshold) return;
    debug(`${name} done in ${duration}ms`);
  }

  async node2xml_(node, width, height) {
    const serializeStart = performance.now();
    let serializedNode = new XMLSerializer().serializeToString(node);
    if (this.xmlPreProcessor_) {
      serializedNode = this.xmlPreProcessor_(serializedNode);
    }
    const result =
        '<div style="' +
        `padding: ${this.margins_}px;` +
        `width: ${width - 2 * this.margins_}px;` +
        `height: ${height - 2 * this.margins_}px;` +
        `">${serializedNode}</div>`;
    this.printTimeSince_(
        'XMLSerializer().serializeToString', serializeStart, 50);
    return result;
  }

  async xml2foreignObjectString_(xml, includeStyle) {
    return ('<foreignObject x="0" y="0" width="100%" height="100%">' +
      '' +
      (includeStyle ? this.styleString_ : '') + xml + '</foreignObject>')
        .replace(/\n/g, ' ')
        .replace(/ +/g, ' ');
  }

  async foreignObjectString2svgElement_(foreignObjectString) {
    const svgElement = document.createElement('svg');
    svgElement.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
    svgElement.innerHTML = foreignObjectString;
    return svgElement;
  }

  async foreignObjectString2svgDataUrl_(foreignObjectString, width, height) {
    let svgString = '<svg xmlns="http://www.w3.org/2000/svg" ' +
        `width="${width}" height="${height}">${foreignObjectString}` + '</svg>';
    svgString = svgString.replace(/\\"/g, "'");
    return 'data:image/svg+xml;utf8,' + encodeURIComponent(svgString);
  }

  async foreignObjectString2imageElement_(foreignObjectString, width, height) {
    const svgDataUrl = await this.foreignObjectString2svgDataUrl_(
        foreignObjectString, width, height);
    return await this.dataUrl2imageElement_(svgDataUrl);
  }

  async dataUrl2imageElement_(dataUrl) {
    const imageElement = document.createElement('img');
    imageElement.src = dataUrl;
    return imageElement;
  }

  async url2imageElement_(url) {
    const imageElement = document.createElement('img');
    imageElement.src = url;
    return imageElement;
  }

  imageElement2canvas_(imageElement, width, height) {
    return new Promise((resolve, reject) => {
      const canvas = document.createElement('canvas');
      const finalWidth = width - (this.cropLeft_ + this.cropRight_);
      const finalHeight = height - (this.cropTop_ + this.cropBottom_);
      canvas.width = this.scale_ * finalWidth;
      canvas.height = this.scale_ * finalHeight;
      const context = canvas.getContext('2d');
      context.scale(this.scale_, this.scale_);
      if (this.disableSmoothing_) {
        context.imageSmoothingEnabled = false;
      }
      this.createImageElementContainer_();
      const onLoad = added => {
        const startDraw = performance.now();
        context.drawImage(
            imageElement,
            // Rectangle from source image:
            this.cropLeft_, this.cropTop_, finalWidth, finalHeight,
            // Rectangle in canvas:
            0, 0, finalWidth, finalHeight);
        this.printTimeSince_('context.drawImage', startDraw, 50);
        if (added) this.imageElementContainer_.removeChild(imageElement);
        resolve(canvas);
      };
      const isLoaded =
          imageElement.complete && imageElement.naturalHeight !== 0;
      if (isLoaded) {
        onLoad(false);
      } else {
        imageElement.addEventListener('load', () => onLoad(true));
        this.imageElementContainer_.appendChild(imageElement);
      }
    });
  }

  async canvas2dataUrl_(canvas) {
    const start = performance.now();
    const pngDataUrl = canvas.toDataURL();
    this.printTimeSince_('canvas.toDataURL', start, 50);
    return pngDataUrl;
  }

  async svgDataUrl2pngDataUrl_(svgDataUrl, width, height) {
    const imageElement = await this.dataUrl2imageElement_(svgDataUrl);
    const canvas = await this.imageElement2canvas_(imageElement, width, height);
    return await this.canvas2dataUrl_(canvas);
  }

  filterOutElements_(cloned) {
    this.selectorsOfElementsToStrip_.forEach(selector => {
      const elements = cloned.querySelectorAll(selector);
      for (let i = 0; i < elements.length; i++) {
        elements[i].parentElement.removeChild(elements[i]);
      }
    });
  }
}
<commit_msg>Hide a recurring but harmless error<commit_after>const INLINE_SVG_REGEX =
    /url\((\\?("|"|'))(data:image\/svg\+xml;utf8,.(((?!\1).)|\\\1)*)\1\)/g;

const EXTERNAL_PNG_REGEX =
    /url\((\\?("|"|'))(.*\.png)\1\)/g;

// Converts map nodes (the entire map or individual tiles) to images.
// Basic mechanism:
//
// 1. Maintain a string with the current style, taken from grid.css and any
//    theme-loaded styles.
// 2. Convert the node to XML string.
// 3. Place the XML string and the maintained style string inside a
//    foreignObject string.
// 4. Place the foreignObject string inside a new SVG string.
// 5. Create an image and set its src to a data URL version of that SVG.
// 6. Draw the image to a canvas, scaling it in the process.
// 7. Use the canvas's toDataUrl() to obtain a PNG data URL.
//
// This is inspired by https://github.com/tsayen/dom-to-image and is similar in
// principle, except that I can get away with not cloning any nodes by
// maintaining the style string and carefully escaping embedded SVGs.
class GridImager {
  constructor(options) {
    this.selectorsOfElementsToStrip_ = options.selectorsOfElementsToStrip || [];
    this.scale_ = options.scale || 1;
    this.disableSmoothing_ = options.disableSmoothing || false;
    this.margins_ = options.margins || 0;
    this.xmlPreProcessor_ = options.xmlPreProcessor || null;
    this.cropLeft_ = options.cropLeft || 0;
    this.cropRight_ = options.cropRight || 0;
    this.cropTop_ = options.cropTop || 0;
    this.cropBottom_ = options.cropBottom || 0;

    this.cssFiles_ = [];   // Order matters!
    this.styleString_ = '';
    this.imageElementContainer_ = null;
  }

  createImageElementContainer_() {
    if (this.imageElementContainer_) return;
    const parent = document.getElementById('mapContainer');
    this.imageElementContainer_ =
        createAndAppendDivWithClass(parent, 'grid-imager-image-container');
  }

  clone(options) {
    const gridImager = new GridImager({
      selectorsOfElementsToStrip:
        options.selectorsOfElementsToStrip || this.selectorsOfElementsToStrip_,
      scale: options.scale || this.scale_,
      disableSmoothing:
          options.disableSmoothing !== undefined ? options.disableSmoothing :
            this.disableSmoothing_,
      margins: options.margins != null ? options.margins : this.margins_,
      xmlPreProcessor: options.xmlPreProcessor || this.xmlPreProcessor_,
      cropLeft: options.cropLeft != null ? options.cropLeft : this.cropLeft_,
      cropRight:
          options.cropRight != null ? options.cropRight : this.cropRight_,
      cropTop: options.cropTop != null ? options.cropTop : this.cropTop_,
      cropBottom:
          options.cropBottom != null ? options.cropBottom : this.cropBottom_,
    });
    gridImager.cssFiles_ = this.cssFiles_;
    gridImager.styleString_ = this.styleString_;
    gridImager.imageElementContainer_ = this.imageElementContainer_;
    return gridImager;
  }

  async addCssStyleSheet(index, cssStyleSheet) {
    if (cssStyleSheet == null) return;
    let cssStr = '';
    const path = cssStyleSheet.href;
    const pathFolder = path.substring(0, path.lastIndexOf('/') + 1);
    for (const rule of cssStyleSheet.cssRules) {
      const selector = rule.selectorText;
      const properties = {};
      for (let i = 0; i < rule.style.length; i++) {
        const propertyName = rule.style.item(i);
        let propertyValue = rule.style.getPropertyValue(propertyName)
            .replace(INLINE_SVG_REGEX, (match, quote, unused, value) =>
              'url("data:image/svg+xml;utf8,' +
                  encodeURIComponent(
                      value.substr(24)
                          .replace(/\\"/g, "'").replace(/%23/g, '#')) + '")');
        propertyValue =
            await this.internExternalImages_(pathFolder, propertyValue);
        properties[propertyName] = propertyValue;
        continue;
      }
      cssStr += `${selector}{`;
      for (const key of Object.keys(properties)) {
        cssStr += `${key}:${properties[key]};`;
      }
      cssStr += '}';
    }
    this.cssFiles_[index] = {path, content: cssStr};
  }

  internExternalImages_(pathFolder, value) {
    this.createImageElementContainer_();
    return new Promise(resolve => {
      const match = EXTERNAL_PNG_REGEX.exec(value);
      if (!match) {
        resolve(value);
        return;
      }
      const src = pathFolder + match[3];
      this.url2imageElement_(src).then(imageElement => {
        imageElement.addEventListener('load', () => {
          const width = imageElement.naturalWidth;
          const height = imageElement.naturalHeight;
          this.imageElement2canvas_(imageElement, width, height)
              .then(canvas => {
                this.imageElementContainer_.removeChild(imageElement);
                this.canvas2dataUrl_(canvas).then(dataUrl => {
                  resolve('url("' + dataUrl + '")');
                });
              });
        });
        this.imageElementContainer_.appendChild(imageElement);
      });
    });
  }

  removeCssFile(path) {
    const indexOfCssFileToRemove =
        this.cssFiles_.findIndex(cssFile => cssFile.path.endsWith(path));
    if (indexOfCssFileToRemove >= 1) {
      this.cssFiles_.splice(indexOfCssFileToRemove, 1);
    }
  }

  recalculateStyleString() {
    this.styleString_ = this.cssFiles_
        .map(cssFile => `<style>${cssFile.content}</style>`)
        .join('\n')
        .replace(/\s+/g, ' ');
  }

  async node2svgElement(node, width, height) {
    width += 2 * this.margins_;
    height += 2 * this.margins_;
    const xml = await this.node2xml_(node, width, height);
    const foreignObjectString = await this.xml2foreignObjectString_(xml, false);
    return await this.foreignObjectString2svgElement_(foreignObjectString);
  }

  async node2svgDataUrl(node, width, height) {
    width += 2 * this.margins_;
    height += 2 * this.margins_;
    const xml = await this.node2xml_(node, width, height);
    const foreignObjectString = await this.xml2foreignObjectString_(xml, true);
    return await this.foreignObjectString2svgDataUrl_(
        foreignObjectString, width, height);
  }

  async node2pngDataUrl(node, width, height) {
    width += 2 * this.margins_;
    height += 2 * this.margins_;
    const xml = await this.node2xml_(node, width, height);
    const foreignObjectString = await this.xml2foreignObjectString_(xml, true);
    const imageElement = await this.foreignObjectString2imageElement_(
        foreignObjectString, width, height);
    const canvas = await this.imageElement2canvas_(imageElement, width, height);
    return await this.canvas2dataUrl_(canvas);
  }

  async node2blob(node, width, height) {
    const pngDataUrl = await this.node2pngDataUrl(node, width, height);
    const binaryString = atob(pngDataUrl.split(',')[1]);
    const length = binaryString.length;
    const binaryArray = new Uint8Array(length);
    for (let i = 0; i < length; i++) {
      binaryArray[i] = binaryString.charCodeAt(i);
    }
    return new Blob([binaryArray], {type: 'image/png'});
  }

  printTimeSince_(name, start, threshold) {
    const duration = Math.round(performance.now() - start);
    if (duration < threshold) return;
    debug(`${name} done in ${duration}ms`);
  }

  async node2xml_(node, width, height) {
    const serializeStart = performance.now();
    let serializedNode = new XMLSerializer().serializeToString(node);
    if (this.xmlPreProcessor_) {
      serializedNode = this.xmlPreProcessor_(serializedNode);
    }
    const result =
        '<div style="' +
        `padding: ${this.margins_}px;` +
        `width: ${width - 2 * this.margins_}px;` +
        `height: ${height - 2 * this.margins_}px;` +
        `">${serializedNode}</div>`;
    this.printTimeSince_(
        'XMLSerializer().serializeToString', serializeStart, 50);
    return result;
  }

  async xml2foreignObjectString_(xml, includeStyle) {
    return ('<foreignObject x="0" y="0" width="100%" height="100%">' +
      '' +
      (includeStyle ? this.styleString_ : '') + xml + '</foreignObject>')
        .replace(/\n/g, ' ')
        .replace(/ +/g, ' ');
  }

  async foreignObjectString2svgElement_(foreignObjectString) {
    const svgElement = document.createElement('svg');
    svgElement.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
    svgElement.innerHTML = foreignObjectString;
    return svgElement;
  }

  async foreignObjectString2svgDataUrl_(foreignObjectString, width, height) {
    let svgString = '<svg xmlns="http://www.w3.org/2000/svg" ' +
        `width="${width}" height="${height}">${foreignObjectString}` + '</svg>';
    svgString = svgString.replace(/\\"/g, "'");
    return 'data:image/svg+xml;utf8,' + encodeURIComponent(svgString);
  }

  async foreignObjectString2imageElement_(foreignObjectString, width, height) {
    const svgDataUrl = await this.foreignObjectString2svgDataUrl_(
        foreignObjectString, width, height);
    return await this.dataUrl2imageElement_(svgDataUrl);
  }

  async dataUrl2imageElement_(dataUrl) {
    const imageElement = document.createElement('img');
    imageElement.src = dataUrl;
    return imageElement;
  }

  async url2imageElement_(url) {
    const imageElement = document.createElement('img');
    imageElement.src = url;
    return imageElement;
  }

  imageElement2canvas_(imageElement, width, height) {
    return new Promise((resolve, reject) => {
      const canvas = document.createElement('canvas');
      const finalWidth = width - (this.cropLeft_ + this.cropRight_);
      const finalHeight = height - (this.cropTop_ + this.cropBottom_);
      canvas.width = this.scale_ * finalWidth;
      canvas.height = this.scale_ * finalHeight;
      const context = canvas.getContext('2d');
      context.scale(this.scale_, this.scale_);
      if (this.disableSmoothing_) {
        context.imageSmoothingEnabled = false;
      }
      this.createImageElementContainer_();
      const onLoad = added => {
        const startDraw = performance.now();
        context.drawImage(
            imageElement,
            // Rectangle from source image:
            this.cropLeft_, this.cropTop_, finalWidth, finalHeight,
            // Rectangle in canvas:
            0, 0, finalWidth, finalHeight);
        this.printTimeSince_('context.drawImage', startDraw, 50);
        if (added) this.imageElementContainer_.removeChild(imageElement);
        resolve(canvas);
      };
      const isLoaded =
          imageElement.complete && imageElement.naturalHeight !== 0;
      if (isLoaded) {
        onLoad(false);
      } else {
        imageElement.addEventListener('load', () => onLoad(true));
        this.imageElementContainer_.appendChild(imageElement);
      }
    });
  }

  async canvas2dataUrl_(canvas) {
    const start = performance.now();
    const pngDataUrl = canvas.toDataURL();
    this.printTimeSince_('canvas.toDataURL', start, 50);
    return pngDataUrl;
  }

  async svgDataUrl2pngDataUrl_(svgDataUrl, width, height) {
    const imageElement = await this.dataUrl2imageElement_(svgDataUrl);
    const canvas = await this.imageElement2canvas_(imageElement, width, height);
    return await this.canvas2dataUrl_(canvas);
  }

  filterOutElements_(cloned) {
    this.selectorsOfElementsToStrip_.forEach(selector => {
      const elements = cloned.querySelectorAll(selector);
      for (let i = 0; i < elements.length; i++) {
        elements[i].parentElement.removeChild(elements[i]);
      }
    });
  }
}
<|endoftext|>


<commit_before>/*
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/

var formdata = {
    "timestamp": {
        "start": null,
        "end": null,
        "duration": null
    },
    "issues": {
        "selected": ''
    },
    "sources": {
        "Television": null,
        "Radio": null,
        "Newspaper": null,
        "Social": null,
        "Online": null,
        "Family": null,
        "Political": null,
        "Elected": null
    },
    "activity": {
        "face": null,
        "message": null,
        "social": null,
        "petition": null,
        "protest": null,
        "volunteerPolitical": null,
        "donatePolitical": null,
        "volunteerCharity": null,
        "donateCharity": null
    },
    "interest": {
        "interest": null
    },
    "personal": {
        "gender": null,
        "yearOfBirth": null,
        "postalCode": null,
        "markedLocation": {
            "lat": null,
            "lng": null
        },
        "twitter": null,
        "email": null,
        "other": null
    },
    "work": {
        "fullTime": null,
        "partTime": null,
        "unemployed": null,
        "disabled": null,
        "undergradStudent": null,
        "mastersStudent": null,
        "phdStudent": null,
        "collegeStudentApprentice": null,
        "home": null,
        "retired": null,
        "undisclosed": null
    }
};

function getDataForSubcategory(category, subcategory) {

    var element = document.querySelector('#' + subcategory);
    var value;

    if (element) {

        /* For radio buttons */
        if (element.selected) {
            value = element.selected;
        }

        /* For slider */
        if (element.value) {
            value = element.value;
        }

        /* For checkboxes */
        if (element.checked !== undefined) {
            value = element.checked;
        }

        if (element.text) {
            value = element.value;
        }

        if (subcategory === 'markedLocation') {
            category[subcategory].lat = element.latitude;
            category[subcategory].lng = element.longitude;
        } else {
            if (category) {
                if (subcategory) {
                    category[subcategory] = value;
                } else {
                    category = value;
                }
            }
        }
    }
}

function setDataForSubcategory(category, subcategory) {
    var element = document.querySelector('#' + subcategory);
    var value;

    if (!element)
        return;

    if (category) {
        if (subcategory) {
            value = category[subcategory];
        } else {
            value = category;
        }
    }

    if (element) {

        /* For radio buttons */
        if (element.localName === 'paper-radio-group') {
            element.selected = value;
        }

        /* For slider */
        if (element.localName === 'paper-slider') {
            element.value = value;
        }

        /* For checkboxes */
        if (element.localName === 'paper-checkbox') {
            element.checked = value;
        }

        /*for input */
        if (element.localName === 'input') {
            element.value = value;
        }

        if (element.localName === 'select'){
            element.value = value;
        }

        if (element.localName === 'google-map-marker') {
            if (value) {
                if (value.lat)
                    element.latitude = value.lat;
                if (value.lng)
                    element.longitude = value.lng;
            }
        }
    }
}

function formdataOperation(operation) {
    for (var category in formdata)
        for (var subcategory in formdata[category]) {
            operation(formdata[category], subcategory);
    }
}

function sendData() {
    var formSubmit = document.querySelector('#formSubmit');
    formSubmit.parms = formdata;
}

(function () {
    'use strict';

    // Grab a reference to our auto-binding template
    // and give it some initial binding values
    // Learn more about auto-binding templates at http://goo.gl/Dx1u2g
    var app = document.querySelector('#app');

    app.displayInstalledToast = function () {
        document.querySelector('#caching-complete').show();
    };

    // Listen for template bound event to know when bindings
    // have resolved and content has been stamped to the page
    app.addEventListener('dom-change', function () {
        console.log('Our app is ready to rock!');
    });

    // See https://github.com/Polymer/polymer/issues/1381
    window.addEventListener('WebComponentsReady', function () {

        // imports are loaded and elements have been registered
        var screenName = document.querySelector("#screenName");

        app.signinvisible = true;

        var formRetrieve = document.querySelector('#formRetrieve');

        if (screenName.textContent) {
            app.signinvisible = false;

            //fire the ajax call to retrieve the data stored
            formRetrieve.generateRequest();
        }

        var consentCheckbox = document.querySelector("#consentCheckbox");

        formRetrieve.addEventListener('response', function (event) {
            if (event.detail.response) {
                consentCheckbox.checked = true;

                var storeddata = event.detail.response;

                for (var category in storeddata) {
                    for (var subcategory in storeddata[category]) {
                        formdata[category][subcategory] = storeddata[category][subcategory];
                    }
                }
            }
        });

        var consentButton = document.querySelector('#consentButton');

        var startingTime;
        var endingTime;

        consentButton.addEventListener('click', function () {

            if (consentCheckbox.checked) {

                startingTime = new Date();

                var timestamp = formdata.timestamp;

                timestamp.start = startingTime.getFullYear() + "-" + startingTime.getMonth() + "-" + startingTime.getDate() + " " + startingTime.getHours() + ":" + startingTime.getMinutes() + ":" + startingTime.getSeconds();

                //set up the formdata at the beginning
                formdataOperation(setDataForSubcategory);

                app.switch();
            } else {
                var toast = document.querySelector('#toaster');
                toast.show();
            }
        });

        var issuesButton = document.querySelector('#issuesButton');

        issuesButton.addEventListener('click', function () {

            app.switch();
        });

        var interestButton = document.querySelector('#interestButton');

        interestButton.addEventListener('click', function () {

            app.switch();
        });

        var sourcesButton = document.querySelector('#sourcesButton');

        sourcesButton.addEventListener('click', function () {

            app.switch();
        });

        var activityButton = document.querySelector('#activityButton');

        activityButton.addEventListener('click', function () {

            app.switch();
        });

        var personalButton = document.querySelector('#personalButton');

        personalButton.addEventListener('click', function () {

            app.switch();
        });

        //initiate the options of the birthDate select
        var birthDay = document.querySelector("#yearOfBirth");

        var years = [];

        for (var year = 1987; year < 1997; year++) {
            years[year - 1987] = year;
        };

        app.set("years", years);

        console.log("app.years " + app.years);

        years.forEach(function (item) {
            var option = document.createElement('option');
            option.textContent = item;
            option.value = item;
            birthDay.appendChild(option);
        });

        var workButton = document.querySelector('#workButton');

        workButton.addEventListener('click', function () {

            app.switch();
        });

        var gmap = document.querySelector('google-map');
        //initiate the map location

       gmap.addEventListener('api-load', function(e) {
            app.lat = 45.387372;
            app.lng = -75.695090;

            navigator.geolocation.getCurrentPosition(function (position) {
                var location = position.coords;

                app.lat = location.latitude;

                app.lng = location.longitude;

                console.log(location);

            });
        });

        gmap.addEventListener('google-map-ready', function () {

            gmap.clickEvents = true;

            gmap._clickEventsChanged();

        });

        gmap.addEventListener('google-map-click', function (event) {
            var location;
            console.log(event);

            app.markerlat = event.detail.latLng.lat();

            app.markerlng = event.detail.latLng.lng();

            console.log(app.markerlat + "," + app.markerlng);

        });

        var formSubmit = document.querySelector('#formSubmit');

        formSubmit.addEventListener('response', function (e) {
            console.log("response from server" + JSON.stringify(e.detail.response));
            app.switch();
        });
        var emailButton = document.querySelector('#emailButton');

        emailButton.addEventListener('click', function () {

            //retrieve the data from the form
            formdataOperation(getDataForSubcategory);

            endingTime = new Date();

            formdata.timestamp.end = endingTime.getFullYear() + "-" + endingTime.getMonth() + "-" + endingTime.getDate() + " " +
                endingTime.getHours() + ":" + endingTime.getMinutes() + ":" + endingTime.getSeconds();

            formdata.timestamp.duration = endingTime - startingTime;

            console.log(formdata);
            formSubmit.body = JSON.stringify(formdata);
            console.log(formSubmit.body);
            formSubmit.generateRequest();

        });

    });
    // Close drawer after menu item is selected if drawerPanel is narrow
    app.onMenuSelect = function () {
        var drawerPanel = document.querySelector('#paperDrawerPanel');
        if (drawerPanel.narrow) {
            drawerPanel.closeDrawer();
        }
    };

    app.switch = function () {
        var pages = document.querySelector('iron-pages');
        pages.selectNext();
    };

    app.addEventListener('switcher', function (e) {
        app.switch();
    });

})();<commit_msg>email validation<commit_after>/*
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/

var formdata = {
    "timestamp": {
        "start": null,
        "end": null,
        "duration": null
    },
    "issues": {
        "selected": ''
    },
    "sources": {
        "Television": null,
        "Radio": null,
        "Newspaper": null,
        "Social": null,
        "Online": null,
        "Family": null,
        "Political": null,
        "Elected": null
    },
    "activity": {
        "face": null,
        "message": null,
        "social": null,
        "petition": null,
        "protest": null,
        "volunteerPolitical": null,
        "donatePolitical": null,
        "volunteerCharity": null,
        "donateCharity": null
    },
    "interest": {
        "interest": null
    },
    "personal": {
        "gender": null,
        "yearOfBirth": null,
        "postalCode": null,
        "markedLocation": {
            "lat": null,
            "lng": null
        },
        "twitter": null,
        "email": null,
        "other": null
    },
    "work": {
        "fullTime": null,
        "partTime": null,
        "unemployed": null,
        "disabled": null,
        "undergradStudent": null,
        "mastersStudent": null,
        "phdStudent": null,
        "collegeStudentApprentice": null,
        "home": null,
        "retired": null,
        "undisclosed": null
    }
};

function getDataForSubcategory(category, subcategory) {

    var element = document.querySelector('#' + subcategory);
    var value;

    if (element) {

        /* For radio buttons */
        if (element.selected) {
            value = element.selected;
        }

        /* For slider */
        if (element.value) {
            value = element.value;
        }

        /* For checkboxes */
        if (element.checked !== undefined) {
            value = element.checked;
        }

        if (element.text) {
            value = element.value;
        }

        if (subcategory === 'markedLocation') {
            category[subcategory].lat = element.latitude;
            category[subcategory].lng = element.longitude;
        } else {
            if (category) {
                if (subcategory) {
                    category[subcategory] = value;
                } else {
                    category = value;
                }
            }
        }
    }
}

function setDataForSubcategory(category, subcategory) {
    var element = document.querySelector('#' + subcategory);
    var value;

    if (!element)
        return;

    if (category) {
        if (subcategory) {
            value = category[subcategory];
        } else {
            value = category;
        }
    }

    if (element) {

        /* For radio buttons */
        if (element.localName === 'paper-radio-group') {
            element.selected = value;
        }

        /* For slider */
        if (element.localName === 'paper-slider') {
            element.value = value;
        }

        /* For checkboxes */
        if (element.localName === 'paper-checkbox') {
            element.checked = value;
        }

        /*for input */
        if (element.localName === 'input') {
            element.value = value;
        }

        if (element.localName === 'select'){
            element.value = value;
        }

        if (element.localName === 'google-map-marker') {
            if (value) {
                if (value.lat)
                    element.latitude = value.lat;
                if (value.lng)
                    element.longitude = value.lng;
            }
        }
    }
}

function formdataOperation(operation) {
    for (var category in formdata)
        for (var subcategory in formdata[category]) {
            operation(formdata[category], subcategory);
    }
}

function sendData() {
    var formSubmit = document.querySelector('#formSubmit');
    formSubmit.parms = formdata;
}

(function () {
    'use strict';

    // Grab a reference to our auto-binding template
    // and give it some initial binding values
    // Learn more about auto-binding templates at http://goo.gl/Dx1u2g
    var app = document.querySelector('#app');

    app.displayInstalledToast = function () {
        document.querySelector('#caching-complete').show();
    };

    // Listen for template bound event to know when bindings
    // have resolved and content has been stamped to the page
    app.addEventListener('dom-change', function () {
        console.log('Our app is ready to rock!');
    });

    // See https://github.com/Polymer/polymer/issues/1381
    window.addEventListener('WebComponentsReady', function () {

        // imports are loaded and elements have been registered
        var screenName = document.querySelector("#screenName");

        app.signinvisible = true;

        var formRetrieve = document.querySelector('#formRetrieve');

        if (screenName.textContent) {
            app.signinvisible = false;

            //fire the ajax call to retrieve the data stored
            formRetrieve.generateRequest();
        }

        var consentCheckbox = document.querySelector("#consentCheckbox");

        formRetrieve.addEventListener('response', function (event) {
            if (event.detail.response) {
                consentCheckbox.checked = true;

                var storeddata = event.detail.response;

                for (var category in storeddata) {
                    for (var subcategory in storeddata[category]) {
                        formdata[category][subcategory] = storeddata[category][subcategory];
                    }
                }
            }
        });

        var consentButton = document.querySelector('#consentButton');

        var startingTime;
        var endingTime;

        consentButton.addEventListener('click', function () {

            if (consentCheckbox.checked) {

                startingTime = new Date();

                var timestamp = formdata.timestamp;

                timestamp.start = startingTime.getFullYear() + "-" + startingTime.getMonth() + "-" + startingTime.getDate() + " " + startingTime.getHours() + ":" + startingTime.getMinutes() + ":" + startingTime.getSeconds();

                //set up the formdata at the beginning
                formdataOperation(setDataForSubcategory);

                app.switch();
            } else {
                var toast = document.querySelector('#toaster');
                toast.show();
            }
        });

        var issuesButton = document.querySelector('#issuesButton');

        issuesButton.addEventListener('click', function () {

            app.switch();
        });

        var interestButton = document.querySelector('#interestButton');

        interestButton.addEventListener('click', function () {

            app.switch();
        });

        var sourcesButton = document.querySelector('#sourcesButton');

        sourcesButton.addEventListener('click', function () {

            app.switch();
        });

        var activityButton = document.querySelector('#activityButton');

        activityButton.addEventListener('click', function () {

            app.switch();
        });

        var personalButton = document.querySelector('#personalButton');

        personalButton.addEventListener('click', function () {

            app.switch();
        });

        //initiate the options of the birthDate select
        var birthDay = document.querySelector("#yearOfBirth");

        var years = [];

        for (var year = 1987; year < 1997; year++) {
            years[year - 1987] = year;
        };

        app.set("years", years);

        console.log("app.years " + app.years);

        years.forEach(function (item) {
            var option = document.createElement('option');
            option.textContent = item;
            option.value = item;
            birthDay.appendChild(option);
        });

        var workButton = document.querySelector('#workButton');

        workButton.addEventListener('click', function () {

            app.switch();
        });

        var gmap = document.querySelector('google-map');
        //initiate the map location

       gmap.addEventListener('api-load', function(e) {
            app.lat = 45.387372;
            app.lng = -75.695090;

            navigator.geolocation.getCurrentPosition(function (position) {
                var location = position.coords;

                app.lat = location.latitude;

                app.lng = location.longitude;

                console.log(location);

            });
        });

        gmap.addEventListener('google-map-ready', function () {

            gmap.clickEvents = true;

            gmap._clickEventsChanged();

        });

        gmap.addEventListener('google-map-click', function (event) {
            var location;
            console.log(event);

            app.markerlat = event.detail.latLng.lat();

            app.markerlng = event.detail.latLng.lng();

            console.log(app.markerlat + "," + app.markerlng);

        });

        var formSubmit = document.querySelector('#formSubmit');

        formSubmit.addEventListener('response', function (e) {
            console.log("response from server" + JSON.stringify(e.detail.response));
            app.switch();
        });

        var email=document.querySelector("#email");

        email.addEventListener('blur', function(event) {
            if(email.validity.typeMismatch) {
                document.querySelector('#emailErr').innerHTML="Please enter a valid eMail address";
            }
        });

        var emailButton = document.querySelector('#emailButton');

        emailButton.addEventListener('click', function () {

            //retrieve the data from the form
            formdataOperation(getDataForSubcategory);

            endingTime = new Date();

            formdata.timestamp.end = endingTime.getFullYear() + "-" + endingTime.getMonth() + "-" + endingTime.getDate() + " " +
                endingTime.getHours() + ":" + endingTime.getMinutes() + ":" + endingTime.getSeconds();

            formdata.timestamp.duration = endingTime - startingTime;

            console.log(formdata);
            formSubmit.body = JSON.stringify(formdata);
            console.log(formSubmit.body);
            formSubmit.generateRequest();

        });

    });
    // Close drawer after menu item is selected if drawerPanel is narrow
    app.onMenuSelect = function () {
        var drawerPanel = document.querySelector('#paperDrawerPanel');
        if (drawerPanel.narrow) {
            drawerPanel.closeDrawer();
        }
    };

    app.switch = function () {
        var pages = document.querySelector('iron-pages');
        pages.selectNext();
    };

    app.addEventListener('switcher', function (e) {
        app.switch();
    });

})();<|endoftext|>


<commit_before>var _ = require('underscore');
var parley = require('parley');
var uuid = require('node-uuid');

// Read global config
var config = require('./config.js');

// Extend adapter definition
var Adapter = module.exports = function (adapter) {
	var self = this;

	// Absorb configuration
	this.config = adapter.config = _.extend({

		// Default transaction collection name
		transactionCollection: _.clone(config.transactionCollection)

	},adapter.config || {});

	this.initialize = function(cb) {
		var self = this;

		// When process ends, close all open connections
		process.on('SIGINT', process.exit);
		process.on('SIGTERM', process.exit);
		process.on('exit', function () { self.teardown(); });

		// Set scheme based on `persistent` options
		this.config.scheme = this.config.persistent ? 'alter' : 'drop';

		adapter.initialize ? adapter.initialize(cb) : cb();
	};

	this.teardown = function (cb) {
		adapter.teardown ? adapter.teardown(cb) : (cb && cb());
	};

	//////////////////////////////////////////////////////////////////////
	// DDL
	//////////////////////////////////////////////////////////////////////
	this.define = function(collectionName, definition, cb) {

		if (!definition.attributes) definition.attributes = {};

		// If id is not defined, add it
		// TODO: Make this check for ANY primary key
		// TODO: Make this disableable in the config
		if (!definition.attributes.id) {
			definition.attributes.id = {
				type: 'INTEGER',
				primaryKey: true,
				autoIncrement: true
			};
		}

		// If the adapter config allows it, and they aren't already specified,
		// extend definition with updatedAt and createdAt
		if(this.config.createdAt && !definition.createdAt) definition.createdAt = 'DATE';
		if(this.config.updatedAt && !definition.updatedAt) definition.updatedAt = 'DATE';

		// Convert string-defined attributes into fully defined objects
		for (var attr in definition.attributes) {
			if(_.isString(definition[attr])) {
				definition[attr] = {
					type: definition[attr]
				};
			}
		}

		// Grab schema from definition
		var schema = definition.attributes;

		// Verify that collection doesn't already exist
		// and then define it and trigger callback
		this.describe(collectionName,function (err,existingSchema) {
			if (err) return cb(err,schema);
			else if (existingSchema) return cb("Trying to define a collection ("+collectionName+") which already exists with schema:",existingSchema);
			else return ( adapter.define ? adapter.define(collectionName,schema,cb) : cb() );
		});
	};

	this.describe = function(collectionName, cb) {
		adapter.describe ? adapter.describe(collectionName,cb) : cb();
	};
	this.drop = function(collectionName, cb) {
		// TODO: foreach through and delete all of the models for this collection
		adapter.drop ? adapter.drop(collectionName,cb) : cb();
	};
	this.alter = function (collectionName,newAttrs,cb) {
		adapter.alter ? adapter.alter(collectionName,newAttrs,cb) : cb();
	};

	//////////////////////////////////////////////////////////////////////
	// DQL
	//////////////////////////////////////////////////////////////////////

	this.create = function(collectionName, values, cb) {
		var self = this;
		if (!collectionName) return cb ("No collectionName specified!");
		if (!adapter.create) return cb("No create() method defined in adapter!");

		adapter.create ? adapter.create(collectionName,values,cb) : cb();

		// TODO: Return model instance Promise object for joins, etc.
	};
	this.find = function(collectionName, options, cb) {
		options = normalizeCriteria(options);
		adapter.find ? adapter.find(collectionName,options,cb) : cb();

		// TODO: Return model instance Promise object for joins, etc.
	};
	this.update = function(collectionName, criteria, values, cb) {
		criteria = normalizeCriteria(criteria);
		adapter.update ? adapter.update(collectionName,criteria,values,cb) : cb();

		// TODO: Return model instance Promise object for joins, etc.
	};
	this.destroy = function(collectionName, criteria, cb) {
		criteria = normalizeCriteria(criteria);
		adapter.destroy ? adapter.destroy(collectionName,criteria,cb) : cb();

		// TODO: Return model instance Promise object for joins, etc.
	};

	//////////////////////////////////////////////////////////////////////
	// Convenience methods (overwritable in adapters)
	//////////////////////////////////////////////////////////////////////
	this.findOrCreate = function (collectionName, criteria, values, cb) {
		var self = this;
		criteria = normalizeCriteria(criteria);
		if (adapter.findOrCreate) adapter.findOrCreate(collectionName, criteria, values, cb);
		else {
			// TODO: ADD A TRANSACTION LOCK HERE!!
			self.find(collectionName,criteria,function (err,results) {
				if (err) cb(err);
				else if (results.length > 0) cb(null,results);
				else self.create(collectionName, values, cb);
			});
		}

		// TODO: Return model instance Promise object for joins, etc.
	};
	this.findAndUpdate = function (collectionName, criteria, values, cb) {
		criteria = normalizeCriteria(criteria);
		if (adapter.findAndUpdate) adapter.findAndUpdate(collectionName, criteria, values, cb);
		else this.update(collectionName, criteria, values, cb);

		// TODO: Return model instance Promise object for joins, etc.
	};
	this.findAndDestroy = function (collectionName, criteria, cb) {
		criteria = normalizeCriteria(criteria);
		if (adapter.findAndDestroy) adapter.findAndDestroy(collectionName, criteria, cb);
		else this.destroy(collectionName, criteria, cb);

		// TODO: Return model instance Promise object for joins, etc.
	};

	// App-level transaction
	this.transaction = function (transactionName, cb) {
		// console.log("Initiating transaction on " + this.identity + "...",transactionName, this.transactionCollection);
		// Generate unique lock and update commit log as if lock was acquired
		var newLock = {
			id: uuid.v4(),
			name: transactionName,
			timestamp: epoch(),
			cb: cb
		};

		var self = this;

		this.transactionCollection.create(newLock,function (err) {
			if (err) return cb(err);

			self.transactionCollection.findAll(function (err,locks) {
				if (err) return cb(err);

				// console.log("wHERE MY LOCKS NIGGA",locks);
				var conflict = false;
				_.each(locks,function (entry) {

					// If a conflict IS found, respect the oldest
					// (the conflict-causer is responsible for cleaning up his entry)
					if (entry.id !== newLock.id &&
						entry.timestamp <= newLock.timestamp) conflict = true;

					// Otherwise, other lock is older-- ignore it
				});

				// Lock acquired!
				if (!conflict) {
					console.log("Acquired lock :: "+newLock.id);
					cb(err, self.unlock);
				}

				// Otherwise, get in line
				// In other words, do nothing--
				// unlock() will grant lock request in order it was received
			});
		});
	};

	this.unlock = function (id,transactionName,cb) {
		var self = this;

		self.transactionCollection.findAll(function (err,locks) {

			// Guess current lock by grabbing the oldest
			// (this will only work if unlock() is used inside of a transaction)
			var currentLock = getOldest(locks);
			if (!currentLock) return cb && cb('Trying to unlock, but no lock exists!');

			// Remove current lock
			self.transactionCollection.destroy({id: id},function (err) {

				// Also remove the lock from the in-memory list
				// (TODO: again suck less and do the right thing)
				locks = _.without(locks,currentLock);

				// Trigger unlock's callback if specified
				cb && cb(err);

				// Now allow the next user in line to acquire the lock (trigger the NEW oldest lock's callback)
				// This marks the end of the previous transaction
				var nextInLine = getOldest(locks);
				nextInLine && nextInLine.cb && nextInLine.cb();
			});
		});
	};

	// TODO: make this suck less
	// (use the [currently unfinished] ORDER option)
	function getOldest(locks) {
		var currentLock;
		_.each(locks,function (lock) {
			if (!currentLock) currentLock = lock;
			else if (lock.timestamp < currentLock.timestamp) currentLock = lock;
		});
		return currentLock;
	}

	// HAX
	// this.lock = this.unlock = function (collectionName, criteria, cb) {
	// 	if (_.isFunction(criteria)) {
	// 		cb = criteria;
	// 		criteria = null;
	// 	}
	// 	cb && cb();
	// };

	// // Begin an atomic transaction
	// // lock models in collection which fit criteria (if criteria is null, lock all)
	// this.lock = function (collectionName, criteria, cb) {

	// 	// Allow criteria argument to be omitted
	// 	if (_.isFunction(criteria)) {
	// 		cb = criteria;
	// 		criteria = null;
	// 	}

	// 	// **************************************
	// 	// NAIVE SOLUTION
	// 	// (only the first roommate to notice gets the milk; the rest wait as soon as they see the note)

	// 	// No need to check the fridge!  Just start writing your note.

	// 	// TODO: Generate identifier for this transaction (use collection name to start with,
	// 		// but better yet, boil down criteria to essentials to allow for more concurrent access)

	// 	// TODO: Create entry in transaction DB (write a note on the fridge and check it)
	// 	// TODO: Check the transaction db (CHECK THE DAMN FRIDGE IN CASE ONE OF YOUR ROOMMATES WROTE THE NOTE WHILE YOU WERE BUSY)

	// 	// TODO: If > 1 entry exists in the transaction db, subscribe to mutex queue to be notified later
	// 	// (if you see a note already on the fridge, get in line to be notified when roommate gets home)

	// 	// TODO: Otherwise, trigger callback!	QA immediately (you're good to go get the milk)

	// 	// **************************************
	// 	// AGRESSIVE SOLUTION
	// 	// (all roommates try to go get the milk, but the first person to get the milk prevents others from putting it in the fridge)

	// 	// TODO: Ask locksmith for model clone
	// 	// TODO: Pass model clone in callback

	// 	adapter.lock ? adapter.lock(collectionName,criteria,cb) : cb();
	// };

	// // Commit and end an atomic transaction
	// // unlock models in collection which fit criteria (if criteria is null, unlock all)
	// this.unlock = function (collectionName, criteria, cb) {

	// 	// Allow criteria argument to be omitted
	// 	if (_.isFunction(criteria)) {
	// 		cb = criteria;
	// 		criteria = null;
	// 	}

	// 	// **************************************
	// 	// NAIVE SOLUTION
	// 	// (only the first roommate to notice gets the milk; the rest wait as soon as they see the note)

	// 	// TODO: Remove entry from transaction db (Remove your note from fridge)
	// 	// TODO: Callback can be triggered immediately, since you're sure the note will be removed

	// 	adapter.unlock ? adapter.unlock(collectionName,criteria,cb) : cb();
	// };

	// this.status = function (collectionName, cb) {
	// 	adapter.status ? adapter.status(collectionName,cb) : cb();
	// };

	// this.autoIncrement = function (collectionName, values,cb) {
	// 	adapter.autoIncrement ? adapter.autoIncrement(collectionName, values, cb) : cb();
	// };

	// If @collectionName and @otherCollectionName are both using this adapter, do a more efficient remote join.
	// (By default, an inner join, but right and left outer joins are also supported.)
	this.join = function(collectionName, otherCollectionName, key, foreignKey, left, right, cb) {
		adapter.join ? adapter.join(collectionName, otherCollectionName, key, foreignKey, left, right, cb) : cb();
	};

	// Sync given collection's schema with the underlying data model
	// Scheme can be 'drop' or 'alter'
	// Controls whether database is dropped and recreated when app starts,
	// or whether waterline will try and synchronize the schema with the app models.
	this.sync = {

		// Drop and recreate collection
		drop: function(collection,cb) {
			var self = this;
			this.drop(collection.identity,function (err,data) {
				if (err) cb(err);
				else self.define(collection.identity,collection,cb);
			});
		},

		// Alter schema
		alter: function(collection, cb) {
			var self = this;

			// Check that collection exists-- if it doesn't go ahead and add it and get out
			this.describe(collection.identity,function (err,data) {
				if (err) return cb(err);
				else if (!data) return self.define(collection.identity,collection,cb);

				// Iterate through each attribute on each model in your app
				_.each(collection.attributes, function checkAttribute(attribute) {
					// and make sure that a comparable field exists in the data store
					// TODO
				});

				// Check that the attribute exists in the data store
				// TODO

				// If not, alter the collection to include it
				// TODO

				// Iterate through each attribute in this collection
				// and make sure that a comparable field exists in the model
				// TODO

				// If not, alter the collection and remove it
				// TODO
				// cb(null);
			});
		}
	};

	// Always grant access to a few of Adapter's methods to the user adapter instance
	// (things that may or may not be defined by the user adapter)
	adapter.transaction = function (name,cb) {
		return self.transaction(name,cb);
	};

	// console.log("--------- ",adapter.transactionCollection);
	// this.transactionCollection = adapter.transactionCollection;
	// adapter.transactionCollection = this.transactionCollection)
	// this.transactionCollection = adapter.transactionCollection || this.transactionCollection;

	// Bind adapter methods to self
	_.bindAll(adapter);
	_.bindAll(this);
	_.bind(this.sync.drop,this);
	_.bind(this.sync.alter,this);

	// Mark as valid adapter
	this._isWaterlineAdapter = true;
};

/**
 * Run a method on an object -OR- each item in an array and return the result
 * Also handle errors gracefully
 */
function plural (collection, application) {
	if(_.isArray(collection)) {
		return _.map(collection, application);
	} else if(_.isObject(collection)) {
		return application(collection);
	} else {
		throw "Invalid collection passed to plural aggreagator:" + collection;
	}
}

// Normalize the different ways of specifying criteria into a uniform object
function normalizeCriteria (criteria) {
	if (!criteria) return {where: null};

	// Empty undefined values from criteria object
	_.each(criteria,function(val,key) {
		if (val === undefined) delete criteria[key];
	});

	if((_.isFinite(criteria) || _.isString(criteria)) && +criteria > 0) {
		criteria = {
			id: +criteria
		};
	}
	if(!_.isObject(criteria)) {
		throw 'Invalid criteria, ' + criteria + ' in find()';
	}
	if (!criteria.where && !criteria.limit &&
		!criteria.skip && !criteria.offset &&
		!criteria.order) {
		criteria = { where: criteria };
	}

	// If any item in criteria is a parsable finite number, use that
	for (var attrName in criteria.where) {
		if (Math.pow(+criteria.where[attrName],2) > 0) {
			criteria.where[attrName] = +criteria.where[attrName];
		}
	}

	return criteria;
}

// Number of miliseconds since the Unix epoch Jan 1st, 1970
function epoch () {
	return (new Date()).getTime();
}<commit_msg>It works?<commit_after>var _ = require('underscore');
var parley = require('parley');
var uuid = require('node-uuid');

// Read global config
var config = require('./config.js');

// Extend adapter definition
var Adapter = module.exports = function (adapter) {
	var self = this;

	// Absorb configuration
	this.config = adapter.config = _.extend({

		// Default transaction collection name
		transactionCollection: _.clone(config.transactionCollection)

	},adapter.config || {});

	this.initialize = function(cb) {
		var self = this;

		// When process ends, close all open connections
		process.on('SIGINT', process.exit);
		process.on('SIGTERM', process.exit);
		process.on('exit', function () { self.teardown(); });

		// Set scheme based on `persistent` options
		this.config.scheme = this.config.persistent ? 'alter' : 'drop';

		adapter.initialize ? adapter.initialize(cb) : cb();
	};

	this.teardown = function (cb) {
		adapter.teardown ? adapter.teardown(cb) : (cb && cb());
	};

	//////////////////////////////////////////////////////////////////////
	// DDL
	//////////////////////////////////////////////////////////////////////
	this.define = function(collectionName, definition, cb) {

		if (!definition.attributes) definition.attributes = {};

		// If id is not defined, add it
		// TODO: Make this check for ANY primary key
		// TODO: Make this disableable in the config
		if (!definition.attributes.id) {
			definition.attributes.id = {
				type: 'INTEGER',
				primaryKey: true,
				autoIncrement: true
			};
		}

		// If the adapter config allows it, and they aren't already specified,
		// extend definition with updatedAt and createdAt
		if(this.config.createdAt && !definition.createdAt) definition.createdAt = 'DATE';
		if(this.config.updatedAt && !definition.updatedAt) definition.updatedAt = 'DATE';

		// Convert string-defined attributes into fully defined objects
		for (var attr in definition.attributes) {
			if(_.isString(definition[attr])) {
				definition[attr] = {
					type: definition[attr]
				};
			}
		}

		// Grab schema from definition
		var schema = definition.attributes;

		// Verify that collection doesn't already exist
		// and then define it and trigger callback
		this.describe(collectionName,function (err,existingSchema) {
			if (err) return cb(err,schema);
			else if (existingSchema) return cb("Trying to define a collection ("+collectionName+") which already exists with schema:",existingSchema);
			else return ( adapter.define ? adapter.define(collectionName,schema,cb) : cb() );
		});
	};

	this.describe = function(collectionName, cb) {
		adapter.describe ? adapter.describe(collectionName,cb) : cb();
	};
	this.drop = function(collectionName, cb) {
		// TODO: foreach through and delete all of the models for this collection
		adapter.drop ? adapter.drop(collectionName,cb) : cb();
	};
	this.alter = function (collectionName,newAttrs,cb) {
		adapter.alter ? adapter.alter(collectionName,newAttrs,cb) : cb();
	};

	//////////////////////////////////////////////////////////////////////
	// DQL
	//////////////////////////////////////////////////////////////////////

	this.create = function(collectionName, values, cb) {
		var self = this;
		if (!collectionName) return cb ("No collectionName specified!");
		if (!adapter.create) return cb("No create() method defined in adapter!");

		adapter.create ? adapter.create(collectionName,values,cb) : cb();

		// TODO: Return model instance Promise object for joins, etc.
	};
	this.find = function(collectionName, options, cb) {
		options = normalizeCriteria(options);
		adapter.find ? adapter.find(collectionName,options,cb) : cb();

		// TODO: Return model instance Promise object for joins, etc.
	};
	this.update = function(collectionName, criteria, values, cb) {
		criteria = normalizeCriteria(criteria);
		adapter.update ? adapter.update(collectionName,criteria,values,cb) : cb();

		// TODO: Return model instance Promise object for joins, etc.
	};
	this.destroy = function(collectionName, criteria, cb) {
		criteria = normalizeCriteria(criteria);
		adapter.destroy ? adapter.destroy(collectionName,criteria,cb) : cb();

		// TODO: Return model instance Promise object for joins, etc.
	};

	//////////////////////////////////////////////////////////////////////
	// Convenience methods (overwritable in adapters)
	//////////////////////////////////////////////////////////////////////
	this.findOrCreate = function (collectionName, criteria, values, cb) {
		var self = this;
		criteria = normalizeCriteria(criteria);
		if (adapter.findOrCreate) adapter.findOrCreate(collectionName, criteria, values, cb);
		else {
			// TODO: ADD A TRANSACTION LOCK HERE!!
			self.find(collectionName,criteria,function (err,results) {
				if (err) cb(err);
				else if (results.length > 0) cb(null,results);
				else self.create(collectionName, values, cb);
			});
		}

		// TODO: Return model instance Promise object for joins, etc.
	};
	this.findAndUpdate = function (collectionName, criteria, values, cb) {
		criteria = normalizeCriteria(criteria);
		if (adapter.findAndUpdate) adapter.findAndUpdate(collectionName, criteria, values, cb);
		else this.update(collectionName, criteria, values, cb);

		// TODO: Return model instance Promise object for joins, etc.
	};
	this.findAndDestroy = function (collectionName, criteria, cb) {
		criteria = normalizeCriteria(criteria);
		if (adapter.findAndDestroy) adapter.findAndDestroy(collectionName, criteria, cb);
		else this.destroy(collectionName, criteria, cb);

		// TODO: Return model instance Promise object for joins, etc.
	};

	// App-level transaction
	this.transaction = function (transactionName, cb) {
		// console.log("Initiating transaction on " + this.identity + "...",transactionName, this.transactionCollection);
		// Generate unique lock and update commit log as if lock was acquired
		var newLock = {
			id: uuid.v4(),
			name: transactionName,
			timestamp: epoch(),
			cb: cb
		};

		var self = this;

		this.transactionCollection.create(newLock,function (err) {
			if (err) return cb(err);

			self.transactionCollection.findAll(function (err,locks) {
				if (err) return cb(err);

				var conflict = false;
				_.each(locks,function (entry) {

					// If a conflict IS found, respect the oldest
					// (the conflict-causer is responsible for cleaning up his entry)
					if (entry.id !== newLock.id &&
						entry.timestamp <= newLock.timestamp) conflict = true;

					// Otherwise, other lock is older-- ignore it
				});

				// Lock acquired!
				if (!conflict) {
					console.log("Acquired lock :: "+newLock.id);
					cb(err, self.unlock);
				}

				// Otherwise, get in line
				// In other words, do nothing--
				// unlock() will grant lock request in order it was received
			});
		});
	};

	this.unlock = function (id,transactionName,cb) {
		var self = this;

		self.transactionCollection.findAll(function (err,locks) {

			// Guess current lock by grabbing the oldest
			// (this will only work if unlock() is used inside of a transaction)
			var currentLock = getOldest(locks);
			if (!currentLock) return cb && cb('Trying to unlock, but no lock exists!');

			// Remove current lock
			self.transactionCollection.destroy({id: id},function (err) {

				// Also remove the lock from the in-memory list
				// (TODO: again suck less and do the right thing)
				locks = _.without(locks,currentLock);

				// Trigger unlock's callback if specified
				cb && cb(err);

				// Now allow the next user in line to acquire the lock (trigger the NEW oldest lock's callback)
				// This marks the end of the previous transaction
				var nextInLine = getOldest(locks);
				nextInLine && nextInLine.cb && nextInLine.cb();
			});
		});
	};

	// TODO: make this suck less
	// (use the [currently unfinished] ORDER option)
	function getOldest(locks) {
		var currentLock;
		_.each(locks,function (lock) {
			if (!currentLock) currentLock = lock;
			else if (lock.timestamp < currentLock.timestamp) currentLock = lock;
		});
		return currentLock;
	}

	// HAX
	// this.lock = this.unlock = function (collectionName, criteria, cb) {
	// 	if (_.isFunction(criteria)) {
	// 		cb = criteria;
	// 		criteria = null;
	// 	}
	// 	cb && cb();
	// };

	// // Begin an atomic transaction
	// // lock models in collection which fit criteria (if criteria is null, lock all)
	// this.lock = function (collectionName, criteria, cb) {

	// 	// Allow criteria argument to be omitted
	// 	if (_.isFunction(criteria)) {
	// 		cb = criteria;
	// 		criteria = null;
	// 	}

	// 	// **************************************
	// 	// NAIVE SOLUTION
	// 	// (only the first roommate to notice gets the milk; the rest wait as soon as they see the note)

	// 	// No need to check the fridge!  Just start writing your note.

	// 	// TODO: Generate identifier for this transaction (use collection name to start with,
	// 		// but better yet, boil down criteria to essentials to allow for more concurrent access)

	// 	// TODO: Create entry in transaction DB (write a note on the fridge and check it)
	// 	// TODO: Check the transaction db (CHECK THE DAMN FRIDGE IN CASE ONE OF YOUR ROOMMATES WROTE THE NOTE WHILE YOU WERE BUSY)

	// 	// TODO: If > 1 entry exists in the transaction db, subscribe to mutex queue to be notified later
	// 	// (if you see a note already on the fridge, get in line to be notified when roommate gets home)

	// 	// TODO: Otherwise, trigger callback!	QA immediately (you're good to go get the milk)

	// 	// **************************************
	// 	// AGRESSIVE SOLUTION
	// 	// (all roommates try to go get the milk, but the first person to get the milk prevents others from putting it in the fridge)

	// 	// TODO: Ask locksmith for model clone
	// 	// TODO: Pass model clone in callback

	// 	adapter.lock ? adapter.lock(collectionName,criteria,cb) : cb();
	// };

	// // Commit and end an atomic transaction
	// // unlock models in collection which fit criteria (if criteria is null, unlock all)
	// this.unlock = function (collectionName, criteria, cb) {

	// 	// Allow criteria argument to be omitted
	// 	if (_.isFunction(criteria)) {
	// 		cb = criteria;
	// 		criteria = null;
	// 	}

	// 	// **************************************
	// 	// NAIVE SOLUTION
	// 	// (only the first roommate to notice gets the milk; the rest wait as soon as they see the note)

	// 	// TODO: Remove entry from transaction db (Remove your note from fridge)
	// 	// TODO: Callback can be triggered immediately, since you're sure the note will be removed

	// 	adapter.unlock ? adapter.unlock(collectionName,criteria,cb) : cb();
	// };

	// this.status = function (collectionName, cb) {
	// 	adapter.status ? adapter.status(collectionName,cb) : cb();
	// };

	// this.autoIncrement = function (collectionName, values,cb) {
	// 	adapter.autoIncrement ? adapter.autoIncrement(collectionName, values, cb) : cb();
	// };

	// If @collectionName and @otherCollectionName are both using this adapter, do a more efficient remote join.
	// (By default, an inner join, but right and left outer joins are also supported.)
	this.join = function(collectionName, otherCollectionName, key, foreignKey, left, right, cb) {
		adapter.join ? adapter.join(collectionName, otherCollectionName, key, foreignKey, left, right, cb) : cb();
	};

	// Sync given collection's schema with the underlying data model
	// Scheme can be 'drop' or 'alter'
	// Controls whether database is dropped and recreated when app starts,
	// or whether waterline will try and synchronize the schema with the app models.
	this.sync = {

		// Drop and recreate collection
		drop: function(collection,cb) {
			var self = this;
			this.drop(collection.identity,function (err,data) {
				if (err) cb(err);
				else self.define(collection.identity,collection,cb);
			});
		},

		// Alter schema
		alter: function(collection, cb) {
			var self = this;

			// Check that collection exists-- if it doesn't go ahead and add it and get out
			this.describe(collection.identity,function (err,data) {
				if (err) return cb(err);
				else if (!data) return self.define(collection.identity,collection,cb);

				// Iterate through each attribute on each model in your app
				_.each(collection.attributes, function checkAttribute(attribute) {
					// and make sure that a comparable field exists in the data store
					// TODO
				});

				// Check that the attribute exists in the data store
				// TODO

				// If not, alter the collection to include it
				// TODO

				// Iterate through each attribute in this collection
				// and make sure that a comparable field exists in the model
				// TODO

				// If not, alter the collection and remove it
				// TODO
				// cb(null);
			});
		}
	};

	// Always grant access to a few of Adapter's methods to the user adapter instance
	// (things that may or may not be defined by the user adapter)
	adapter.transaction = function (name,cb) {
		return self.transaction(name,cb);
	};

	// console.log("--------- ",adapter.transactionCollection);
	// this.transactionCollection = adapter.transactionCollection;
	// adapter.transactionCollection = this.transactionCollection)
	// this.transactionCollection = adapter.transactionCollection || this.transactionCollection;

	// Bind adapter methods to self
	_.bindAll(adapter);
	_.bindAll(this);
	_.bind(this.sync.drop,this);
	_.bind(this.sync.alter,this);

	// Mark as valid adapter
	this._isWaterlineAdapter = true;
};

/**
 * Run a method on an object -OR- each item in an array and return the result
 * Also handle errors gracefully
 */
function plural (collection, application) {
	if(_.isArray(collection)) {
		return _.map(collection, application);
	} else if(_.isObject(collection)) {
		return application(collection);
	} else {
		throw "Invalid collection passed to plural aggreagator:" + collection;
	}
}

// Normalize the different ways of specifying criteria into a uniform object
function normalizeCriteria (criteria) {
	if (!criteria) return {where: null};

	// Empty undefined values from criteria object
	_.each(criteria,function(val,key) {
		if (val === undefined) delete criteria[key];
	});

	if((_.isFinite(criteria) || _.isString(criteria)) && +criteria > 0) {
		criteria = {
			id: +criteria
		};
	}
	if(!_.isObject(criteria)) {
		throw 'Invalid criteria, ' + criteria + ' in find()';
	}
	if (!criteria.where && !criteria.limit &&
		!criteria.skip && !criteria.offset &&
		!criteria.order) {
		criteria = { where: criteria };
	}

	// If any item in criteria is a parsable finite number, use that
	for (var attrName in criteria.where) {
		if (Math.pow(+criteria.where[attrName],2) > 0) {
			criteria.where[attrName] = +criteria.where[attrName];
		}
	}

	return criteria;
}

// Number of miliseconds since the Unix epoch Jan 1st, 1970
function epoch () {
	return (new Date()).getTime();
}<|endoftext|>


<commit_before>(function( $ ) {

    var months = new Array(
        "January",
        "February",
        "March",
        "April",
        "May",
        "June",
        "July",
        "August",
        "September",
        "October",
        "November",
        "December"
    );

    $.fn.calendar = function(year, month, minDate, meetings, inputName) {
        var calendar = this;

        var today = new Date;
        if (year == null) year = today.getFullYear();
        if (month == null) month = today.getMonth();
        if (minDate == null) minDate = today;

        calendar.year = year;
        calendar.month = month;
        calendar.minDate = minDate;
        calendar.meetings = meetings;
        calendar.selected = new Array();
        calendar.inputName = inputName;

        var prevBtn = $('<');
        var monthLbl = $('');
        var yearLbl = $('');
        var nextBtn = $('>');

        this.showMonth = function (year, month) {
            var firstDay = new Date(year, month, 1);
            var monthOffset = firstDay.getDay() - 1;
            firstDay = new Date(year, month, -monthOffset);

            var lastDay = new Date(year, month + 1, 0);

            if (minDate < firstDay)
                prevBtn.removeClass('disabled');
            else
                prevBtn.addClass('disabled');

            calendar.days.empty();
            var week = $("
");
            var curr = firstDay;
            while (curr <= lastDay) {

                var d = $("");

                if (curr.getMonth() == month) {
                    var n = $("
");
                    var v = curr.toISOString().slice(0, 10);
                    n.attr('id', 'calendar-date-' + v);
                    n.addClass('calendar-date');
                    n.addClass('calendar-month-' + (curr.getMonth()));
                    n.addClass('calendar-day-' + (curr.getDay()));
                    n.data('date', v);
                    n.text(curr.getDate());

                    if (curr == today)
                        n.addClass('calendar-date-today');

                    if (curr < minDate)
                        n.addClass('disabled');
                    else
                        n.addClass('enabled');

                    if (!(v in meetings)) {
                        n.addClass('calendar-date-filled');
                    }

                    n.on('click', function(e) {
                        e.preventDefault();

                        var n = $(this);
                        var v = $(this).data('date');
                        var i = $.inArray(v, calendar.selected);
                        var inputId = calendar.inputName + '-' + v;

                        if (i == -1) {
                            n.addClass('calendar-date-selected');
                            calendar.selected.push(v);

                            var input = $("<input type='hidden' name='"+ calendar.inputName +"[]'/>");
                            input.attr('id', inputId);
                            input.val(v);
                            calendar.append(input);

                        } else {
                            n.removeClass('calendar-date-selected');
                            calendar.selected.splice(i, 1 );
                            $("#"+inputId).remove();
                        }

                    });

                    d.append(n);
                }

                week.append(d);
                if (curr.getDay() == 6) {
                    calendar.days.append(week);
                    week = $("
");
                }

                curr.setDate(curr.getDate() + 1);
            }
            calendar.days.append(week);
            monthLbl.text(months[month]);
            yearLbl.text(year);

            calendar.year = year;
            calendar.month = month;
        };

        prevBtn.on('click', function(e) {
            e.preventDefault();
            if (prevBtn.hasClass('disabled'))
                return;

            var month = calendar.month - 1;
            var year = calendar.year;
            if (month < 0) {
                month = 11;
                year = calendar.year - 1;
            }
            calendar.showMonth(year, month);
        });

        nextBtn.on('click', function(e) {
            e.preventDefault();
            if (nextBtn.hasClass('disabled'))
                return;

            var month = calendar.month + 1;
            var year = calendar.year;
            if (month > 11) {
                month = 0;
                year = calendar.year + 1;
            }
            calendar.showMonth(year, month);
        });

        this.days = $('

');

        var head = $('
');
        head.append(prevBtn);
        head.append(monthLbl);
        head.append(yearLbl);
        head.append(nextBtn);
        this.append(head);

        var table = $('

' +
            'Sun	Mon	Tue	' +
            'Wed	Thu	Fri	Sat	' +
            '

');
        table.append(this.days);
        this.append(table);

        this.showMonth(year, month);
    }
}( jQuery ));
<commit_msg>Fix dateFormat error in calendar<commit_after>(function( $ ) {

    var months = new Array(
        "January",
        "February",
        "March",
        "April",
        "May",
        "June",
        "July",
        "August",
        "September",
        "October",
        "November",
        "December"
    );

    function dateFormat(date) {
        var m = date.getMonth()+1;
        var d = date.getDate();
        return date.getFullYear() + '-' + (m < 10 ? '0'+m : m) + '-' + (d < 10 ? '0'+d : d);
    }

    $.fn.calendar = function(year, month, minDate, meetings, inputName) {
        var calendar = this;

        var today = new Date;
        if (year == null) year = today.getFullYear();
        if (month == null) month = today.getMonth();
        if (minDate == null) minDate = today;

        calendar.year = year;
        calendar.month = month;
        calendar.minDate = minDate;
        calendar.meetings = meetings;
        calendar.selected = new Array();
        calendar.inputName = inputName;

        var prevBtn = $('<');
        var monthLbl = $('');
        var yearLbl = $('');
        var nextBtn = $('>');

        this.showMonth = function (year, month) {
            var firstDay = new Date(year, month, 1);
            var monthOffset = firstDay.getDay() - 1;
            firstDay = new Date(year, month, -monthOffset);

            var lastDay = new Date(year, month + 1, 0);

            if (minDate < firstDay)
                prevBtn.removeClass('disabled');
            else
                prevBtn.addClass('disabled');

            calendar.days.empty();
            var week = $("
");
            var curr = firstDay;
            while (curr <= lastDay) {

                var d = $("");

                if (curr.getMonth() == month) {
                    var n = $("
");
                    var v = dateFormat(curr);
                    n.attr('id', 'calendar-date-' + v);
                    n.addClass('calendar-date');
                    n.addClass('calendar-month-' + (curr.getMonth()));
                    n.addClass('calendar-day-' + (curr.getDay()));
                    n.data('date', v);
                    n.text(curr.getDate());

                    if (curr == today)
                        n.addClass('calendar-date-today');

                    if (curr < minDate)
                        n.addClass('disabled');
                    else
                        n.addClass('enabled');

                    if (v in meetings)
                        n.addClass('calendar-date-filled');

                    n.on('click', function(e) {
                        e.preventDefault();

                        var n = $(this);
                        var v = $(this).data('date');
                        var i = $.inArray(v, calendar.selected);
                        var inputId = calendar.inputName + '-' + v;

                        if (i == -1) {
                            n.addClass('calendar-date-selected');
                            calendar.selected.push(v);

                            var input = $("<input type='hidden' name='"+ calendar.inputName +"[]'/>");
                            input.attr('id', inputId);
                            input.val(v);
                            calendar.append(input);

                        } else {
                            n.removeClass('calendar-date-selected');
                            calendar.selected.splice(i, 1 );
                            $("#"+inputId).remove();
                        }

                    });

                    d.append(n);
                }

                week.append(d);
                if (curr.getDay() == 6) {
                    calendar.days.append(week);
                    week = $("
");
                }

                curr.setDate(curr.getDate() + 1);
            }
            calendar.days.append(week);
            monthLbl.text(months[month]);
            yearLbl.text(year);

            calendar.year = year;
            calendar.month = month;
        };

        prevBtn.on('click', function(e) {
            e.preventDefault();
            if (prevBtn.hasClass('disabled'))
                return;

            var month = calendar.month - 1;
            var year = calendar.year;
            if (month < 0) {
                month = 11;
                year = calendar.year - 1;
            }
            calendar.showMonth(year, month);
        });

        nextBtn.on('click', function(e) {
            e.preventDefault();
            if (nextBtn.hasClass('disabled'))
                return;

            var month = calendar.month + 1;
            var year = calendar.year;
            if (month > 11) {
                month = 0;
                year = calendar.year + 1;
            }
            calendar.showMonth(year, month);
        });

        this.days = $('

');

        var head = $('
');
        head.append(prevBtn);
        head.append(monthLbl);
        head.append(yearLbl);
        head.append(nextBtn);
        this.append(head);

        var table = $('

' +
            'Sun	Mon	Tue	' +
            'Wed	Thu	Fri	Sat	' +
            '

');
        table.append(this.days);
        this.append(table);

        this.showMonth(year, month);
    }
}( jQuery ));
<|endoftext|>