<commit_before>define(['../dispatch'], function(dispatch) {

  'use strict';

  function split_contents(formatVersion, segment, entries, offset) {
    switch (formatVersion) {
      default: return Promise.reject('unsupported format version: ' + formatVersion);
      case 6:
        return segment.getBytes(offset + 8, 2).then(function(count) {
          count = count[0] | (count[1] << 8);
          var totalSize = 6 /* signature */
            + 1 /* modifier */
            + 1 /* padding? */
            + 2 /* file count */
            + 13 /* password dooberry */
            + count*13 /* file names */
            + count*4 /* file lengths */
            + count*2 /* flags & ratio */;
          var fileListSegment = segment.getSegment('chunk/ags; which=file-list-v6; base=' + (offset + totalSize), offset, totalSize);
          entries.add(fileListSegment);
        });
      case 10:
      case 11:
      case 15:
        return segment.getBytes(0, 5).then(function(bytes) {
          if (bytes[0] !== 0) return Promise.reject('not first datafile in chain');
          var containerCount = (bytes[1] | (bytes[2] << 8) | (bytes[3] << 16) | (bytes[4] << 24)) >>> 0;
          return segment.getBytes(1 + 4 + 20*containerCount, 4).then(function(bytes) {
            var fileCount = (bytes[0] | (bytes[1] << 8) | (bytes[2] << 16) | (bytes[3] << 24)) >>> 0;
            var totalSize = 1 + 4 + 20*containerCount + 4 + 25*fileCount + 4*fileCount + 4*fileCount + fileCount;
            entries.add(segment.getSegment('chunk/ags; which=file-list-v' + formatVersion, 0, totalSize));
          });
        });
      case 20:
        return segment.getDataView(0, 5).then(function(dv) {
          if (dv.getUint8(0) !== 0) return Promise.reject('not first datafile in chain');
          var pos = 5;
          function doFileNames(fileCount) {
            if (fileCount === 0) return;
            return segment.getInt16(pos, true)
            .then(function(nameLength) {
              nameLength = (nameLength / 5) | 0;
              pos += 4 + nameLength;
              return doFileNames(fileCount - 1);
            });
          }
          function doFiles(fileCount) {
            return doFileNames(fileCount).then(function() {
              pos += fileCount*4 + fileCount*4 + fileCount;
            });
          }
          function doContainers(containerCount) {
            if (containerCount === 0) {
              return segment.getInt32(pos, true).then(function(fileCount) {
                pos += 4;
                doFiles(fileCount);
              });
            }
            function onByte(b) {
              pos++;
              if (b === 0) return doContainers(containerCount - 1);
              return segment.getUint8(pos).then(onByte);
            }
            return segment.getUint8(pos).then(onByte);
          }
          return doContainers(dv.getUint32(1, true))
          .then(function() {
            entries.add(segment.getSegment('chunk/ags; which=file-list-v' + formatVersion), 0, pos);
          });
        });
    }
  }

  function split_prefix(segment, entries, offset) {
    return segment.getBytes(offset, 6).then(function(prefix) {
      if (String.fromCharCode(prefix[0], prefix[1], prefix[2], prefix[3]) !== 'CLIB') {
        return Promise.reject('resource package signature not found');
      }
      return split_contents(
        prefix[5],
        segment.getSegment(segment.typeName + '; body-version=' + prefix[5]),
        entries,
        offset);
    });
  }

  function split_suffix(segment, entries) {
    return segment.getBytes('suffix', 16).then(function(suffix) {
      var signature = String.fromCharCode.apply(null, suffix.subarray(4, 16));
      if (signature !== 'CLIB\x01\x02\x03\x04SIGE') {
        return Promise.reject('resource package signature not found');
      }
      var offset = (suffix[0] | (suffix[1] << 8) | (suffix[2] << 16) | (suffix[3] << 24)) >>> 0;
      return split_prefix(segment, entries, offset);
    });
  }

  function split(segment, entries) {
    var bodyVersion = segment.getTypeParameter('body-version');
    if (bodyVersion) return split_contents(bodyVersion, segment, entries, 0);

    var mode = segment.getTypeParameter('mode');
    if (mode === 'prefix') return split_prefix(segment, entries, 0);
    if (mode === 'suffix') return split_suffix(segment, entries);

    return split_prefix(segment, entries, 0).then(null, function() {
      return split_suffix(segment, entries);
    });
  }

  function mount(containerSegment, volume) {
    var promiseChain = Promise.resolve();
    return containerSegment.split(function(chunk) {
      if (chunk.typeName === 'chunk/ags' && chunk.getTypeParameter('which') === 'file-list-v6') {
        var base = chunk.getTypeParameter('base');
        if (!base) {
          throw new Error('base must be set');
        }
        base = +base;
        promiseChain = promiseChain.then(chunk.getStruct())
        .then(function(fileList) {
          for (var i = 0; i < fileList.fileRecords.length; i++) {
            var file = fileList.fileRecords[i];
            var fileType;
            var ext = file.name.match(/[^\.]*$/)[0].toLowerCase();
            switch (ext) {
              case 'crm':
                fileType = 'application/x-ags-room';
                break;
              case 'mid':
                fileType = 'audio/midi';
                break;
              case 'wav':
                fileType = 'audio/x-wav';
                break;
              case 'dta':
                fileType = 'application/x-ags-game';
                break;
              case 'spr':
                fileType = 'application/x-ags-sprite-pack';
                break;
              case 'wfn':
                fileType = 'application/x-ags-font';
                break;
              case 'dat':
                if (/^aclang.*\.dat$/i.test(file.name)) {
                  fileType = 'application/x-ags-translation';
                  break;
                }
                else {
                  console.log('unknown file: ' + file.name);
                  fileType = 'application/octet-stream';
                }
                break;
              default:
                fileType = dispatch.byExtension[ext] || 'application/octet-stream';
                console.log('unknown file: ' + file.name);
                break;
            }
            volume.addFile(containerSegment.getSegment(fileType, file.byteOffset, file.byteLength));
          }
        });

      }
      else {
        console.log('unsupported: ' + chunk.type);
      }
    },
    function() {
      return promiseChain;
    });
  }

  return {
    split: split,
    mount: mount,
  };

});
<commit_msg>Update x-ags-container.js<commit_after>define(['../dispatch'], function(dispatch) {

  'use strict';

  function split_contents(formatVersion, segment, entries, offset) {
    switch (formatVersion) {
      default: return Promise.reject('unsupported format version: ' + formatVersion);
      case 6:
        return segment.getBytes(offset + 8, 2).then(function(count) {
          count = count[0] | (count[1] << 8);
          var totalSize = 6 /* signature */
            + 1 /* modifier */
            + 1 /* padding? */
            + 2 /* file count */
            + 13 /* password dooberry */
            + count*13 /* file names */
            + count*4 /* file lengths */
            + count*2 /* flags & ratio */;
          var fileListSegment = segment.getSegment('chunk/ags; which=file-list-v6; base=' + (offset + totalSize), offset, totalSize);
          entries.add(fileListSegment);
        });
      case 10:
      case 11:
      case 15:
        return segment.getBytes(0, 5).then(function(bytes) {
          if (bytes[0] !== 0) return Promise.reject('not first datafile in chain');
          var containerCount = (bytes[1] | (bytes[2] << 8) | (bytes[3] << 16) | (bytes[4] << 24)) >>> 0;
          return segment.getBytes(1 + 4 + 20*containerCount, 4).then(function(bytes) {
            var fileCount = (bytes[0] | (bytes[1] << 8) | (bytes[2] << 16) | (bytes[3] << 24)) >>> 0;
            var totalSize = 1 + 4 + 20*containerCount + 4 + 25*fileCount + 4*fileCount + 4*fileCount + fileCount;
            entries.add(segment.getSegment('chunk/ags; which=file-list-v' + formatVersion, 0, totalSize));
          });
        });
      case 20:
        return segment.getDataView(0, 5).then(function(dv) {
          if (dv.getUint8(0) !== 0) return Promise.reject('not first datafile in chain');
          var pos = 5;
          function doFileNames(fileCount) {
            if (fileCount === 0) return;
            return segment.getInt16(pos, true)
            .then(function(nameLength) {
              nameLength = (nameLength / 5) | 0;
              pos += 4 + nameLength;
              return doFileNames(fileCount - 1);
            });
          }
          function doFiles(fileCount) {
            return doFileNames(fileCount).then(function() {
              pos += fileCount*4 + fileCount*4 + fileCount;
            });
          }
          function doContainers(containerCount) {
            if (containerCount === 0) {
              return segment.getInt32(pos, true).then(function(fileCount) {
                pos += 4;
                doFiles(fileCount);
              });
            }
            function onByte(b) {
              pos++;
              if (b === 0) return doContainers(containerCount - 1);
              return segment.getUint8(pos).then(onByte);
            }
            return segment.getUint8(pos).then(onByte);
          }
          return doContainers(dv.getUint32(1, true))
          .then(function() {
            entries.add(segment.getSegment('chunk/ags; which=file-list-v' + formatVersion), 0, pos);
          });
        });
    }
  }

  function split_prefix(segment, entries, offset) {
    return segment.getBytes(offset, 6).then(function(prefix) {
      if (String.fromCharCode(prefix[0], prefix[1], prefix[2], prefix[3]) !== 'CLIB') {
        return Promise.reject('resource package signature not found');
      }
      return split_contents(
        prefix[5],
        segment.getSegment(segment.typeName + '; body-version=' + prefix[5]),
        entries,
        offset);
    });
  }

  function split_suffix(segment, entries) {
    return segment.getBytes('suffix', 16).then(function(suffix) {
      var signature = String.fromCharCode.apply(null, suffix.subarray(4, 16));
      if (signature !== 'CLIB\x01\x02\x03\x04SIGE') {
        return Promise.reject('resource package signature not found');
      }
      var offset = (suffix[0] | (suffix[1] << 8) | (suffix[2] << 16) | (suffix[3] << 24)) >>> 0;
      return split_prefix(segment, entries, offset);
    });
  }

  function split(segment, entries) {
    var bodyVersion = segment.getTypeParameter('body-version');
    if (bodyVersion) return split_contents(bodyVersion, segment, entries, 0);

    var mode = segment.getTypeParameter('mode');
    if (mode === 'prefix') return split_prefix(segment, entries, 0);
    if (mode === 'suffix') return split_suffix(segment, entries);

    return split_prefix(segment, entries, 0).then(null, function() {
      return split_suffix(segment, entries);
    });
  }

  function mount(containerSegment, volume) {
    var promiseChain = Promise.resolve();
    return containerSegment.split(function(chunk) {
      if (chunk.typeName === 'chunk/ags' && chunk.getTypeParameter('which') === 'file-list-v6') {
        var base = chunk.getTypeParameter('base');
        if (!base) {
          throw new Error('base must be set');
        }
        base = +base;
        promiseChain = promiseChain.then(
        chunk.getStruct()
        .then(function(fileList) {
          for (var i = 0; i < fileList.fileRecords.length; i++) {
            var file = fileList.fileRecords[i];
            var fileType;
            var ext = file.name.match(/[^\.]*$/)[0].toLowerCase();
            switch (ext) {
              case 'crm':
                fileType = 'application/x-ags-room';
                break;
              case 'mid':
                fileType = 'audio/midi';
                break;
              case 'wav':
                fileType = 'audio/x-wav';
                break;
              case 'dta':
                fileType = 'application/x-ags-game';
                break;
              case 'spr':
                fileType = 'application/x-ags-sprite-pack';
                break;
              case 'wfn':
                fileType = 'application/x-ags-font';
                break;
              case 'dat':
                if (/^aclang.*\.dat$/i.test(file.name)) {
                  fileType = 'application/x-ags-translation';
                  break;
                }
                else {
                  console.log('unknown file: ' + file.name);
                  fileType = 'application/octet-stream';
                }
                break;
              default:
                fileType = dispatch.byExtension[ext] || 'application/octet-stream';
                console.log('unknown file: ' + file.name);
                break;
            }
            volume.addFile(containerSegment.getSegment(fileType, file.byteOffset, file.byteLength));
          }
        }));
      }
      else {
        console.log('unsupported: ' + chunk.type);
      }
    },
    function() {
      return promiseChain;
    });
  }

  return {
    split: split,
    mount: mount,
  };

});
<|endoftext|>


<commit_before>import Vue from 'vue'

import QBtn from '../btn/QBtn.js'
import QIcon from '../icon/QIcon.js'
import QSpinner from '../spinner/QSpinner.js'
import QCircularProgress from '../circular-progress/QCircularProgress.js'

import FileMixin from '../../mixins/file.js'
import DarkMixin from '../../mixins/dark.js'

import { stop } from '../../utils/event.js'
import { humanStorageSize } from '../../utils/format.js'
import { cache } from '../../utils/vm.js'

export default Vue.extend({
  name: 'QUploaderBase',

  mixins: [ DarkMixin, FileMixin ],

  props: {
    label: String,

    color: String,
    textColor: String,

    square: Boolean,
    flat: Boolean,
    bordered: Boolean,

    noThumbnails: Boolean,
    autoUpload: Boolean,
    hideUploadBtn: Boolean,

    disable: Boolean,
    readonly: Boolean
  },

  provide () {
    return {
      __qUploaderGetInput: this.__getInputControl
    }
  },

  data () {
    return {
      files: [],
      queuedFiles: [],
      uploadedFiles: [],
      dnd: false,
      expanded: false,

      uploadSize: 0,
      uploadedSize: 0
    }
  },

  watch: {
    isUploading (newVal, oldVal) {
      if (oldVal === false && newVal === true) {
        this.$emit('start')
      }
      else if (oldVal === true && newVal === false) {
        this.$emit('finish')
      }
    }
  },

  computed: {
    /*
     * When extending:
     *   Required : isUploading
     *   Optional: isBusy
     */

    canUpload () {
      return this.editable === true &&
        this.isBusy !== true &&
        this.isUploading !== true &&
        this.queuedFiles.length > 0
    },

    canAddFiles () {
      return this.editable &&
        this.isUploading !== true &&
        (this.multiple === true || this.queuedFiles.length === 0)
    },

    extensions () {
      if (this.accept !== void 0) {
        return this.accept.split(',').map(ext => {
          ext = ext.trim()
          // support "image/*"
          if (ext.endsWith('/*')) {
            ext = ext.slice(0, ext.length - 1)
          }
          return ext
        })
      }
    },

    uploadProgress () {
      return this.uploadSize === 0
        ? 0
        : this.uploadedSize / this.uploadSize
    },

    uploadProgressLabel () {
      return this.__getProgressLabel(this.uploadProgress)
    },

    uploadedSizeLabel () {
      return humanStorageSize(this.uploadedSize)
    },

    uploadSizeLabel () {
      return humanStorageSize(this.uploadSize)
    },

    colorClass () {
      const cls = []
      this.color !== void 0 && cls.push(`bg-${this.color}`)
      this.textColor !== void 0 && cls.push(`text-${this.textColor}`)
      return cls.join(' ')
    },

    editable () {
      return this.disable !== true && this.readonly !== true
    }
  },

  methods: {
    reset () {
      if (!this.disable) {
        this.abort()
        this.uploadedSize = 0
        this.uploadSize = 0
        this.__revokeImgURLs()
        this.files = []
        this.queuedFiles = []
        this.uploadedFiles = []
      }
    },

    removeUploadedFiles () {
      if (!this.disable) {
        this.files = this.files.filter(f => {
          if (f.__status !== 'uploaded') {
            return true
          }

          f._img !== void 0 && window.URL.revokeObjectURL(f._img.src)

          return false
        })
        this.uploadedFiles = []
      }
    },

    removeQueuedFiles () {
      if (!this.disable) {
        const removedFiles = []

        const files = this.files.filter(f => {
          if (f.__status !== 'idle' && f.__status !== 'failed') {
            return true
          }

          this.uploadSize -= f.size
          removedFiles.push(f)

          f._img !== void 0 && window.URL.revokeObjectURL(f._img.src)

          return false
        })

        if (removedFiles.length > 0) {
          this.files = files
          this.queuedFiles = []
          this.$emit('removed', removedFiles)
        }
      }
    },

    removeFile (file) {
      if (this.disable) { return }

      if (file.__status === 'uploaded') {
        this.uploadedFiles = this.uploadedFiles.filter(f => f.name !== file.name)
      }
      else if (file.__status === 'uploading') {
        file.__abort()
      }
      else {
        this.uploadSize -= file.size
      }

      this.files = this.files.filter(f => {
        if (f.name !== file.name) {
          return true
        }

        f._img !== void 0 && window.URL.revokeObjectURL(f._img.src)

        return false
      })
      this.queuedFiles = this.queuedFiles.filter(f => f.name !== file.name)
      this.$emit('removed', [ file ])
    },

    __revokeImgURLs () {
      this.files.forEach(f => {
        f._img !== void 0 && window.URL.revokeObjectURL(f._img.src)
      })
    },

    __getFileInput () {
      return this.$refs.input ||
        this.$el.getElementsByClassName('q-uploader__input')[0]
    },

    __getProgressLabel (p) {
      return (p * 100).toFixed(2) + '%'
    },

    __updateFile (file, status, uploadedSize) {
      file.__status = status

      if (status === 'idle') {
        file.__uploaded = 0
        file.__progress = 0
        file.__sizeLabel = humanStorageSize(file.size)
        file.__progressLabel = '0.00%'
        return
      }
      if (status === 'failed') {
        this.$forceUpdate()
        return
      }

      file.__uploaded = status === 'uploaded'
        ? file.size
        : uploadedSize

      file.__progress = status === 'uploaded'
        ? 1
        : Math.min(0.9999, file.__uploaded / file.size)

      file.__progressLabel = this.__getProgressLabel(file.__progress)
      this.$forceUpdate()
    },

    __addFiles (e, fileList) {
      const files = this.__processFiles(e, fileList)
      this.__getFileInput().value = ''

      if (files === void 0) { return }

      files.forEach(file => {
        this.__updateFile(file, 'idle')
        this.uploadSize += file.size

        if (this.noThumbnails !== true && file.type.toUpperCase().startsWith('IMAGE')) {
          const img = new Image()
          img.src = window.URL.createObjectURL(file)
          file.__img = img
        }
      })

      this.files = this.files.concat(files)
      this.queuedFiles = this.queuedFiles.concat(files)
      this.$emit('added', files)
      this.autoUpload === true && this.upload()
    },

    __getBtn (h, show, icon, fn) {
      if (show === true) {
        return h(QBtn, {
          props: {
            type: 'a',
            icon: this.$q.iconSet.uploader[icon],
            flat: true,
            dense: true
          },
          on: icon === 'add' ? null : { click: fn }
        }, icon === 'add' ? this.__getInputControl(h) : null)
      }
    },

    __getInputControl (h) {
      return [
        h('input', {
          ref: 'input',
          staticClass: 'q-uploader__input overflow-hidden absolute-full',
          attrs: {
            tabindex: -1,
            type: 'file',
            title: '', // try to remove default tooltip
            accept: this.accept,
            ...(this.multiple === true ? { multiple: true } : {})
          },
          on: cache(this, 'input', {
            mousedown: stop, // need to stop refocus from QBtn
            change: this.__addFiles
          })
        })
      ]
    },

    __getHeader (h) {
      if (this.$scopedSlots.header !== void 0) {
        return this.$scopedSlots.header(this)
      }

      return [
        h('div', {
          staticClass: 'q-uploader__header-content flex flex-center no-wrap q-gutter-xs'
        }, [
          this.__getBtn(h, this.queuedFiles.length > 0, 'removeQueue', this.removeQueuedFiles),
          this.__getBtn(h, this.uploadedFiles.length > 0, 'removeUploaded', this.removeUploadedFiles),

          this.isUploading === true
            ? h(QSpinner, { staticClass: 'q-uploader__spinner' })
            : null,

          h('div', { staticClass: 'col column justify-center' }, [
            this.label !== void 0
              ? h('div', { staticClass: 'q-uploader__title' }, [ this.label ])
              : null,

            h('div', { staticClass: 'q-uploader__subtitle' }, [
              this.uploadSizeLabel + ' / ' + this.uploadProgressLabel
            ])
          ]),

          this.__getBtn(h, this.canAddFiles, 'add', this.pickFiles),
          this.__getBtn(h, this.hideUploadBtn === false && this.canUpload === true, 'upload', this.upload),
          this.__getBtn(h, this.isUploading, 'clear', this.abort)
        ])
      ]
    },

    __getList (h) {
      if (this.$scopedSlots.list !== void 0) {
        return this.$scopedSlots.list(this)
      }

      return this.files.map(file => h('div', {
        key: file.name,
        staticClass: 'q-uploader__file relative-position',
        class: {
          'q-uploader__file--img': this.noThumbnails !== true && file.__img !== void 0,
          'q-uploader__file--failed': file.__status === 'failed',
          'q-uploader__file--uploaded': file.__status === 'uploaded'
        },
        style: this.noThumbnails !== true && file.__img !== void 0 ? {
          backgroundImage: 'url(' + file.__img.src + ')'
        } : null
      }, [
        h('div', {
          staticClass: 'q-uploader__file-header row flex-center no-wrap'
        }, [
          file.__status === 'failed'
            ? h(QIcon, {
              staticClass: 'q-uploader__file-status',
              props: {
                name: this.$q.iconSet.type.negative,
                color: 'negative'
              }
            })
            : null,

          h('div', { staticClass: 'q-uploader__file-header-content col' }, [
            h('div', { staticClass: 'q-uploader__title' }, [ file.name ]),
            h('div', {
              staticClass: 'q-uploader__subtitle row items-center no-wrap'
            }, [
              file.__sizeLabel + ' / ' + file.__progressLabel
            ])
          ]),

          file.__status === 'uploading'
            ? h(QCircularProgress, {
              props: {
                value: file.__progress,
                min: 0,
                max: 1,
                indeterminate: file.__progress === 0
              }
            })
            : h(QBtn, {
              props: {
                round: true,
                dense: true,
                flat: true,
                icon: this.$q.iconSet.uploader[file.__status === 'uploaded' ? 'done' : 'clear']
              },
              on: {
                click: () => { this.removeFile(file) }
              }
            })
        ])
      ]))
    }
  },

  beforeDestroy () {
    this.isUploading === true && this.abort()
    this.files.length > 0 && this.__revokeImgURLs()
  },

  render (h) {
    const children = [
      h('div', {
        staticClass: 'q-uploader__header',
        class: this.colorClass
      }, this.__getHeader(h)),

      h('div', {
        staticClass: 'q-uploader__list scroll'
      }, this.__getList(h)),

      this.__getDnd(h, 'uploader')
    ]

    this.isBusy === true && children.push(
      h('div', {
        staticClass: 'q-uploader__overlay absolute-full flex flex-center'
      }, [ h(QSpinner) ])
    )

    return h('div', {
      staticClass: 'q-uploader column no-wrap',
      class: {
        'q-uploader--dark q-dark': this.isDark,
        'q-uploader--bordered': this.bordered,
        'q-uploader--square no-border-radius': this.square,
        'q-uploader--flat no-shadow': this.flat,
        'disabled q-uploader--disable': this.disable
      },
      on: this.canAddFiles === true
        ? cache(this, 'drag', { dragover: this.__onDragOver })
        : null
    }, children)
  }
})
<commit_msg>fix(QUploader):  Uploading same file twice (#6537) (#6556)<commit_after>import Vue from 'vue'

import QBtn from '../btn/QBtn.js'
import QIcon from '../icon/QIcon.js'
import QSpinner from '../spinner/QSpinner.js'
import QCircularProgress from '../circular-progress/QCircularProgress.js'

import FileMixin from '../../mixins/file.js'
import DarkMixin from '../../mixins/dark.js'

import { stop } from '../../utils/event.js'
import { humanStorageSize } from '../../utils/format.js'
import { cache } from '../../utils/vm.js'

export default Vue.extend({
  name: 'QUploaderBase',

  mixins: [ DarkMixin, FileMixin ],

  props: {
    label: String,

    color: String,
    textColor: String,

    square: Boolean,
    flat: Boolean,
    bordered: Boolean,

    noThumbnails: Boolean,
    autoUpload: Boolean,
    hideUploadBtn: Boolean,

    disable: Boolean,
    readonly: Boolean
  },

  provide () {
    return {
      __qUploaderGetInput: this.__getInputControl
    }
  },

  data () {
    return {
      files: [],
      queuedFiles: [],
      uploadedFiles: [],
      dnd: false,
      expanded: false,

      uploadSize: 0,
      uploadedSize: 0
    }
  },

  watch: {
    isUploading (newVal, oldVal) {
      if (oldVal === false && newVal === true) {
        this.$emit('start')
      }
      else if (oldVal === true && newVal === false) {
        this.$emit('finish')
      }
    }
  },

  computed: {
    /*
     * When extending:
     *   Required : isUploading
     *   Optional: isBusy
     */

    canUpload () {
      return this.editable === true &&
        this.isBusy !== true &&
        this.isUploading !== true &&
        this.queuedFiles.length > 0
    },

    canAddFiles () {
      return this.editable &&
        this.isUploading !== true &&
        (this.multiple === true || this.queuedFiles.length === 0)
    },

    extensions () {
      if (this.accept !== void 0) {
        return this.accept.split(',').map(ext => {
          ext = ext.trim()
          // support "image/*"
          if (ext.endsWith('/*')) {
            ext = ext.slice(0, ext.length - 1)
          }
          return ext
        })
      }
    },

    uploadProgress () {
      return this.uploadSize === 0
        ? 0
        : this.uploadedSize / this.uploadSize
    },

    uploadProgressLabel () {
      return this.__getProgressLabel(this.uploadProgress)
    },

    uploadedSizeLabel () {
      return humanStorageSize(this.uploadedSize)
    },

    uploadSizeLabel () {
      return humanStorageSize(this.uploadSize)
    },

    colorClass () {
      const cls = []
      this.color !== void 0 && cls.push(`bg-${this.color}`)
      this.textColor !== void 0 && cls.push(`text-${this.textColor}`)
      return cls.join(' ')
    },

    editable () {
      return this.disable !== true && this.readonly !== true
    }
  },

  methods: {
    reset () {
      if (!this.disable) {
        this.abort()
        this.uploadedSize = 0
        this.uploadSize = 0
        this.__revokeImgURLs()
        this.files = []
        this.queuedFiles = []
        this.uploadedFiles = []
      }
    },

    removeUploadedFiles () {
      if (!this.disable) {
        this.files = this.files.filter(f => {
          if (f.__status !== 'uploaded') {
            return true
          }

          f._img !== void 0 && window.URL.revokeObjectURL(f._img.src)

          return false
        })
        this.uploadedFiles = []
      }
    },

    removeQueuedFiles () {
      if (!this.disable) {
        const removedFiles = []

        const files = this.files.filter(f => {
          if (f.__status !== 'idle' && f.__status !== 'failed') {
            return true
          }

          this.uploadSize -= f.size
          removedFiles.push(f)

          f._img !== void 0 && window.URL.revokeObjectURL(f._img.src)

          return false
        })

        if (removedFiles.length > 0) {
          this.files = files
          this.queuedFiles = []
          this.$emit('removed', removedFiles)
        }
      }
    },

    removeFile (file) {
      if (this.disable) { return }

      if (file.__status === 'uploaded') {
        this.uploadedFiles = this.uploadedFiles.filter(f => f.name !== file.name)
      }
      else if (file.__status === 'uploading') {
        file.__abort()
      }
      else {
        this.uploadSize -= file.size
      }

      this.files = this.files.filter(f => {
        if (f.name !== file.name) {
          return true
        }

        f._img !== void 0 && window.URL.revokeObjectURL(f._img.src)

        return false
      })
      this.queuedFiles = this.queuedFiles.filter(f => f.name !== file.name)
      this.$emit('removed', [ file ])
    },

    __revokeImgURLs () {
      this.files.forEach(f => {
        f._img !== void 0 && window.URL.revokeObjectURL(f._img.src)
      })
    },

    __getFileInput () {
      return this.$refs.input ||
        this.$el.getElementsByClassName('q-uploader__input')[0]
    },

    __getProgressLabel (p) {
      return (p * 100).toFixed(2) + '%'
    },

    __updateFile (file, status, uploadedSize) {
      file.__status = status

      if (status === 'idle') {
        file.__uploaded = 0
        file.__progress = 0
        file.__sizeLabel = humanStorageSize(file.size)
        file.__progressLabel = '0.00%'
        return
      }
      if (status === 'failed') {
        this.$forceUpdate()
        return
      }

      file.__uploaded = status === 'uploaded'
        ? file.size
        : uploadedSize

      file.__progress = status === 'uploaded'
        ? 1
        : Math.min(0.9999, file.__uploaded / file.size)

      file.__progressLabel = this.__getProgressLabel(file.__progress)
      this.$forceUpdate()
    },

    __addFiles (e, fileList) {
      const files = this.__processFiles(e, fileList)
        .filter(file => this.files.findIndex(f => file.name === f.name) === -1)

      this.__getFileInput().value = ''

      if (files === void 0) { return }

      files.forEach(file => {
        this.__updateFile(file, 'idle')
        this.uploadSize += file.size

        if (this.noThumbnails !== true && file.type.toUpperCase().startsWith('IMAGE')) {
          const img = new Image()
          img.src = window.URL.createObjectURL(file)
          file.__img = img
        }
      })

      this.files = this.files.concat(files)
      this.queuedFiles = this.queuedFiles.concat(files)
      this.$emit('added', files)
      this.autoUpload === true && this.upload()
    },

    __getBtn (h, show, icon, fn) {
      if (show === true) {
        return h(QBtn, {
          props: {
            type: 'a',
            icon: this.$q.iconSet.uploader[icon],
            flat: true,
            dense: true
          },
          on: icon === 'add' ? null : { click: fn }
        }, icon === 'add' ? this.__getInputControl(h) : null)
      }
    },

    __getInputControl (h) {
      return [
        h('input', {
          ref: 'input',
          staticClass: 'q-uploader__input overflow-hidden absolute-full',
          attrs: {
            tabindex: -1,
            type: 'file',
            title: '', // try to remove default tooltip
            accept: this.accept,
            ...(this.multiple === true ? { multiple: true } : {})
          },
          on: cache(this, 'input', {
            mousedown: stop, // need to stop refocus from QBtn
            change: this.__addFiles
          })
        })
      ]
    },

    __getHeader (h) {
      if (this.$scopedSlots.header !== void 0) {
        return this.$scopedSlots.header(this)
      }

      return [
        h('div', {
          staticClass: 'q-uploader__header-content flex flex-center no-wrap q-gutter-xs'
        }, [
          this.__getBtn(h, this.queuedFiles.length > 0, 'removeQueue', this.removeQueuedFiles),
          this.__getBtn(h, this.uploadedFiles.length > 0, 'removeUploaded', this.removeUploadedFiles),

          this.isUploading === true
            ? h(QSpinner, { staticClass: 'q-uploader__spinner' })
            : null,

          h('div', { staticClass: 'col column justify-center' }, [
            this.label !== void 0
              ? h('div', { staticClass: 'q-uploader__title' }, [ this.label ])
              : null,

            h('div', { staticClass: 'q-uploader__subtitle' }, [
              this.uploadSizeLabel + ' / ' + this.uploadProgressLabel
            ])
          ]),

          this.__getBtn(h, this.canAddFiles, 'add', this.pickFiles),
          this.__getBtn(h, this.hideUploadBtn === false && this.canUpload === true, 'upload', this.upload),
          this.__getBtn(h, this.isUploading, 'clear', this.abort)
        ])
      ]
    },

    __getList (h) {
      if (this.$scopedSlots.list !== void 0) {
        return this.$scopedSlots.list(this)
      }

      return this.files.map(file => h('div', {
        key: file.name,
        staticClass: 'q-uploader__file relative-position',
        class: {
          'q-uploader__file--img': this.noThumbnails !== true && file.__img !== void 0,
          'q-uploader__file--failed': file.__status === 'failed',
          'q-uploader__file--uploaded': file.__status === 'uploaded'
        },
        style: this.noThumbnails !== true && file.__img !== void 0 ? {
          backgroundImage: 'url(' + file.__img.src + ')'
        } : null
      }, [
        h('div', {
          staticClass: 'q-uploader__file-header row flex-center no-wrap'
        }, [
          file.__status === 'failed'
            ? h(QIcon, {
              staticClass: 'q-uploader__file-status',
              props: {
                name: this.$q.iconSet.type.negative,
                color: 'negative'
              }
            })
            : null,

          h('div', { staticClass: 'q-uploader__file-header-content col' }, [
            h('div', { staticClass: 'q-uploader__title' }, [ file.name ]),
            h('div', {
              staticClass: 'q-uploader__subtitle row items-center no-wrap'
            }, [
              file.__sizeLabel + ' / ' + file.__progressLabel
            ])
          ]),

          file.__status === 'uploading'
            ? h(QCircularProgress, {
              props: {
                value: file.__progress,
                min: 0,
                max: 1,
                indeterminate: file.__progress === 0
              }
            })
            : h(QBtn, {
              props: {
                round: true,
                dense: true,
                flat: true,
                icon: this.$q.iconSet.uploader[file.__status === 'uploaded' ? 'done' : 'clear']
              },
              on: {
                click: () => { this.removeFile(file) }
              }
            })
        ])
      ]))
    }
  },

  beforeDestroy () {
    this.isUploading === true && this.abort()
    this.files.length > 0 && this.__revokeImgURLs()
  },

  render (h) {
    const children = [
      h('div', {
        staticClass: 'q-uploader__header',
        class: this.colorClass
      }, this.__getHeader(h)),

      h('div', {
        staticClass: 'q-uploader__list scroll'
      }, this.__getList(h)),

      this.__getDnd(h, 'uploader')
    ]

    this.isBusy === true && children.push(
      h('div', {
        staticClass: 'q-uploader__overlay absolute-full flex flex-center'
      }, [ h(QSpinner) ])
    )

    return h('div', {
      staticClass: 'q-uploader column no-wrap',
      class: {
        'q-uploader--dark q-dark': this.isDark,
        'q-uploader--bordered': this.bordered,
        'q-uploader--square no-border-radius': this.square,
        'q-uploader--flat no-shadow': this.flat,
        'disabled q-uploader--disable': this.disable
      },
      on: this.canAddFiles === true
        ? cache(this, 'drag', { dragover: this.__onDragOver })
        : null
    }, children)
  }
})
<|endoftext|>


<commit_before>// TIMELINE Module

// Load templates
const timelineTemplate = require('../templates/playlist-timeline.hbs');

/**
 * Timeline contructor
 * @param {object} container - the container to render the timeline to
 * @param {object =} [options] - Timeline options
 */
let PlaylistTimeline = function(container) {
    this.DOMObject = container;
};

/**
 * Render Timeline and the layout
 */
PlaylistTimeline.prototype.render = function() {

    // Render timeline template
    const html = timelineTemplate(pE.playlist);

    // Append html to the main div
    this.DOMObject.html(html);

    // Enable select for each widget
    this.DOMObject.find('.playlist-widget.selectable').click(function(e) {
        e.stopPropagation();
        if(!$(this).hasClass('to-be-saved')) {
            pE.selectObject($(this));
        }
    });

    this.DOMObject.find('.timeline-overlay-step').droppable({
        greedy: true,
        accept: '[drop-to="region"]',
        drop: function(event, ui) {
            const position = parseInt($(event.target).data('position')) + 1;

            pE.playlist.addElement(event.target, ui.draggable[0], position);
        }
    });

    this.DOMObject.find('.timeline-overlay-step').click(function(e) {
        if(!$.isEmptyObject(pE.toolbar.selectedCard) || !$.isEmptyObject(pE.toolbar.selectedQueue)) {
            e.stopPropagation();
            const position = parseInt($(this).data('position')) + 1;

            pE.selectObject($(this).parents('#playlist-timeline'), false, {positionToAdd: position});
        }
    });

    this.DOMObject.find('.playlist-widget').droppable({
        greedy: true,
        accept: function(el) {
            return ($(this).hasClass('editable') && $(el).attr('drop-to') === 'widget') ||
                ($(this).hasClass('permissionsModifiable') && $(el).attr('drop-to') === 'all' && $(el).data('subType') === 'permissions');
        },
        drop: function(event, ui) {
            pE.playlist.addElement(event.target, ui.draggable[0]);
        }
    });

    // Handle widget attached audio click
    this.DOMObject.find('.playlist-widget.editable .editProperty').click(function(e) {
        e.stopPropagation();

        const widget = pE.getElementByTypeAndId($(this).parents('.playlist-widget').data('type'), $(this).parents('.playlist-widget').attr('id'), $(this).parents('.playlist-widget').data('widgetRegion'));

        widget.editPropertyForm($(this).data('property'), $(this).data('propertyType'));
    });

    this.DOMObject.find('.playlist-widget').contextmenu(function(ev) {

        if($(ev.currentTarget).is('.editable, .deletable, .permissionsModifiable')) {
            // Open context menu
            pE.openContextMenu(ev.currentTarget, {
                x: ev.pageX,
                y: ev.pageY
            });
        }

        // Prevent browser menu to open
        return false;
    });

    // Save order function with debounce
    var saveOrderFunc = _.debounce(function() {
        pE.saveOrder();
        pE.timeline.DOMObject.find('#unsaved').hide();
        pE.timeline.DOMObject.find('#saved').show();
    }, 1000);

    // Sortable widgets
    this.DOMObject.find('#timeline-container').sortable({
        start: function(event, ui) {
            pE.timeline.DOMObject.find('#unsaved').hide();
            saveOrderFunc.cancel();
        },
        stop: function(event, ui) {
            // Mark target as "to be saved"
            $(ui.item).addClass('to-be-saved');

            pE.timeline.DOMObject.find('#unsaved').show();
            saveOrderFunc();
        }
    });
};

module.exports = PlaylistTimeline;
<commit_msg>Playlist Timeline: Rearranging Widget order using drag and drop is occasionally not working fixes xibosignage/xibo#2255<commit_after>// TIMELINE Module

// Load templates
const timelineTemplate = require('../templates/playlist-timeline.hbs');

/**
 * Timeline contructor
 * @param {object} container - the container to render the timeline to
 * @param {object =} [options] - Timeline options
 */
let PlaylistTimeline = function(container) {
    this.DOMObject = container;
};

/**
 * Render Timeline and the layout
 */
PlaylistTimeline.prototype.render = function() {

    // Render timeline template
    const html = timelineTemplate(pE.playlist);

    // Append html to the main div
    this.DOMObject.html(html);

    // Enable select for each widget
    this.DOMObject.find('.playlist-widget.selectable').click(function(e) {
        e.stopPropagation();
        if(!$(this).hasClass('to-be-saved')) {
            pE.selectObject($(this));
        }
    });

    this.DOMObject.find('.timeline-overlay-step').droppable({
        greedy: true,
        accept: '[drop-to="region"]',
        drop: function(event, ui) {
            const position = parseInt($(event.target).data('position')) + 1;

            pE.playlist.addElement(event.target, ui.draggable[0], position);
        }
    });

    this.DOMObject.find('.timeline-overlay-step').click(function(e) {
        if(!$.isEmptyObject(pE.toolbar.selectedCard) || !$.isEmptyObject(pE.toolbar.selectedQueue)) {
            e.stopPropagation();
            const position = parseInt($(this).data('position')) + 1;

            pE.selectObject($(this).parents('#playlist-timeline'), false, {positionToAdd: position});
        }
    });

    this.DOMObject.find('.playlist-widget').droppable({
        greedy: true,
        accept: function(el) {
            return ($(this).hasClass('editable') && $(el).attr('drop-to') === 'widget') ||
                ($(this).hasClass('permissionsModifiable') && $(el).attr('drop-to') === 'all' && $(el).data('subType') === 'permissions');
        },
        drop: function(event, ui) {
            pE.playlist.addElement(event.target, ui.draggable[0]);
        }
    });

    // Handle widget attached audio click
    this.DOMObject.find('.playlist-widget.editable .editProperty').click(function(e) {
        e.stopPropagation();

        const widget = pE.getElementByTypeAndId($(this).parents('.playlist-widget').data('type'), $(this).parents('.playlist-widget').attr('id'), $(this).parents('.playlist-widget').data('widgetRegion'));

        widget.editPropertyForm($(this).data('property'), $(this).data('propertyType'));
    });

    this.DOMObject.find('.playlist-widget').contextmenu(function(ev) {

        if($(ev.currentTarget).is('.editable, .deletable, .permissionsModifiable')) {
            // Open context menu
            pE.openContextMenu(ev.currentTarget, {
                x: ev.pageX,
                y: ev.pageY
            });
        }

        // Prevent browser menu to open
        return false;
    });

    // Save order function with debounce
    var saveOrderFunc = _.debounce(function() {
        pE.saveOrder();
        pE.timeline.DOMObject.find('#unsaved').hide();
        pE.timeline.DOMObject.find('#saved').show();
    }, 1000);

    // Sortable widgets
    this.DOMObject.find('#timeline-container').sortable({
        axis: 'y',
        items: '.playlist-widget',
        start: function(event, ui) {
            pE.timeline.DOMObject.find('#unsaved').hide();
            saveOrderFunc.cancel();
            pE.clearTemporaryData();
        },
        stop: function(event, ui) {
            // Mark target as "to be saved"
            $(ui.item).addClass('to-be-saved');

            pE.timeline.DOMObject.find('#unsaved').show();
            saveOrderFunc();
        }
    });
};

module.exports = PlaylistTimeline;
<|endoftext|>


<commit_before>// match a pattern bypassing strings (double or single quote, or both)
// NB: as of this version, escaped patterns are not checked
module.exports.match = function (/* start, end, stringQuotes, handler */) {
	var args = this._helper_setArguments([null,null,['"',"'"]], arguments, 'match')
	var start = args[0]
		, end = args[1]
		, stringQuotes = args[2]
		, handler = args[3]

	if (start === null || end === null)
		throw new Error('match(): start and end required')

	if ( !isArray(stringQuotes) )
		throw new Error('match(): stringQuotes must be an Array')

	var atok = this

	var current = atok.helpersCache.match || ''
	var count = atok.helpersCache.matchCount || 0
	var quotesNum = stringQuotes.length

	function matchAcc (data) {
		current += data
	}
	function matchWaitString (len) {
		atok.seek(-len)
		atok.helpersCache.match = current
		atok.helpersCache.matchCount = count
	}
	function matchStartEnd (token, idx) {
		// If quiet(true) do not keep the data
		if (typeof token !== 'number') matchAcc(token)
		count += idx > 0 ? -1 : 1
	}
	function matchSkipString (token) {
		if (typeof token !== 'number') matchAcc(token)
	}
	function matchDone () {
		atok.seek(-1) // Get back by one as it was artificially added
		handler(current)
		atok.helpersCache.match = ''
		atok.helpersCache.matchCount = 0
	}

	atok
		.saveProps('match')
		.next()
		.continue(1)
			// Start found, apply the helper rules
			.addRule(start, function matchInit () { count = 1 })
		// 6=noop + start/end + noop + match-not-done + match-done + acc
		.continue( 2*quotesNum + 6 )
			// No start found, end now
			.noop()
		.continue(1)
			// Check start or end patterns
			.addRule([start, end], matchStartEnd)
			// start/end not found
		.continue(2)
			.noop()
		// start/end found, check if we are done
		.ignore(true)
			// Not done
			.continue(-3)
				.addRule(function () {console.log('xx',count); return count > 0 ? 0 : -1 }, 'match-not-done')
			// Done
			.continue( 2*quotesNum + 1 )
				.addRule(_true, 'match-done')
		.ignore()

	// Skip strings content
	if (quotesNum > 0) {
		atok.escaped(true).trim()

		for (var i = 0; i < quotesNum; i++)
			atok
				.continue( -(i + 5) )
					// Full string found
					.addRule(stringQuotes[i], stringQuotes[i], matchSkipString)
					// Partial string found, hold on for the end of the string
					// and resume at the string check rule
				.break(true).continue(-2).quiet(true)
					.addRule(stringQuotes[i], matchWaitString)
				.break().quiet()

		atok.escaped().trim(true)
	}

	return atok
		.continue( -(2*quotesNum + 5) )
			// Store data and go back to start/end check
			.addRule(1, matchAcc)
		// If data found, send it
		.loadProps('match')
		// If 0 is returned and continue(), the rule index is not reset
		// => return 1 and seek(-1) in the handler!
		.addRule(function () { return count > 0 ? -1 : 1 }, matchDone)
}
<commit_msg>cleanup<commit_after><|endoftext|>


<commit_before>var fs = require('fs');
var crc32 = require('crc32');
var msgpack = require('msgpack-js');

var sizes = {
  timestamp: 32,
  keysize: 16,
  valsize: 32,
  crc: 32,
  header: 32 + 32 + 16 + 32 // crc + timestamp + keysize + valsize
};

var tombstone = 'medea_tombstone';

var KeyDirEntry = function() {
  this.fileId = null;
  this.valueSize = null;
  this.valuePosition = null;
  this.timestamp = null;
};

var FileInfo = function() {
  this.filename = null;
  this.fd = null;
};

var Medea = module.exports = function(options) {
  this.active = null;
  this.keydir = {};

  options = options || {};

  this.maxFileSize = options.hasOwnProperty('maxFileSize')
    ? options.maxFileSize : 2147483648; //2GB

  this.mergeWindow = options.hasOwnProperty('mergeWindow')
    ? options.mergeWindow : 'always';

  this.fragMergeTrigger = options.hasOwnProperty('fragMergeTrigger')
    ? options.fragMergeTrigger : 60; // fragmentation >= 60%

  this.deadBytesMergeTrigger = options.hasOwnProperty('deadBytesMergeTrigger')
    ? options.deadBytesMergeTrigger : 536870912; // dead bytes > 512MB

  this.fragThreshold = options.hasOwnProperty('fragThreshold')
    ? options.fragThreshold : 40; // fragmentation >= 40%

  this.deadBytesThreshold = options.hasOwnProperty('deadBytesThreshold')
    ? options.deadBytesThreshold : 134217728; // dead bytes > 128MB

  this.smallFileThreshold = options.hasOwnProperty('smallFileThreshold')
    ? options.smallFileThreshold : 10485760 // file < 10MB

  this.maxFoldAge = options.hasOwnProperty('maxFoldAge') ? options.maxFoldAge : -1;
  this.maxFoldPuts = options.hasOwnProperty('maxFoldPuts') ? options.maxFoldPuts : 0;
  this.expirySecs = options.hasOwnProperty('expirySecs') ? options.expirySecs : -1;
};

Medea.prototype.open = function(dir, cb) {
  if (typeof dir === 'function') {
    cb = dir;
    dir = 'medea';
  }

  this.active = new FileInfo();
  this.active.filename =  dir + '/' + Date.now().toString() + '.medea.data';

  dir = __dirname + '/' + dir;
  var that = this;
  fs.stat(dir, function(err, stat) {
    if (!stat) {
      fs.mkdir(dir, function(err) {
        that._open(dir, cb);
      });
    } else {
      that._open(dir, cb);
    }
  });
};

Medea.prototype._open = function(dir, cb) {
  var file = this.active;
  fs.open(this.active.filename, 'a+', function(err, value) {
    file.fd = value;
    cb();
  });
};

Medea.prototype.close = function(cb) {
  if (this.active.fd) {
    fs.close(this.active.fd, cb);
  } else {
    cb();
  }
};

Medea.prototype.put = function(k, v, cb) {
  var that = this;
  fs.stat(this.active.filename, function(err, stat) {
    var offset = stat ? stat.size : 0;
    that._put(k, v, offset, cb);
  });
};

Medea.prototype._put = function(k, v, offset, cb) {
  var packed = msgpack.encode(v);

  var ts = Date.now();

  var crc = new Buffer(sizes.crc);
  var timestamp = new Buffer(sizes.timestamp);
  var keysz = new Buffer(sizes.keysize);
  var valuesz = new Buffer(sizes.valsize);
  var key = new Buffer(k);
  var value = new Buffer(packed);

  timestamp.write(ts.toString());
  keysz.write(key.length.toString());
  valuesz.write(value.length.toString());

  var bufs = Buffer.concat([timestamp, keysz, valuesz, key, value]);
  crc.write(crc32(bufs));

  var line = Buffer.concat([crc, bufs]);

  this._write(line, offset);

  var entry = new KeyDirEntry();
  entry.fileId = this._fileTimestamp(this.active.filename);
  entry.valueSize = value.length;
  entry.valuePosition = offset + sizes.header + key.length;
  entry.timestamp = ts;

  this.keydir[k] = entry;

  cb();
};

Medea.prototype._fileTimestamp = function(file) {
  var f = file.replace('\\', '/');
  var lastSlashLocation = file.lastIndexOf('/');
  var dotLocation = file.indexOf('.');
  var ts = file.substring(lastSlashLocation+1, dotLocation);
  return Number(ts);
};

Medea.prototype._write = function(bufs, offset) {
  var stream = fs.createWriteStream(this.active.filename, { flags: 'a', fd: this.active.fd });
  stream.write(bufs);
};

Medea.prototype.get = function(key, cb) {
  var entry = this.keydir[key];
  if (entry) {
    var readBuffer = new Buffer(entry.valueSize);
    fs.read(this.active.fd, readBuffer, 0, entry.valueSize, entry.valuePosition, function(err, bytesRead, buffer) {
      var msg = msgpack.decode(buffer);
      if (msg !== tombstone) {
        cb(msg);
      } else {
        cb();
      }
    });

    // Not working properly on node v0.8.x
    /*var options = {
      flags: 'r',
      autoClose: false,
      start: entry.valuePosition,
      end: entry.valuePosition + entry.valueSize,
      fd: this.active.fd
    }

    var stream = fs.createReadStream(this.active.filename, options);
    var bufs = [];
    var len = 0;

    stream.on('data', function(data) {
      len += data.length;
      bufs.push(data);
    });

    stream.on('end', function(data) {
      var i = 0;
      var body = new Buffer(len);
      bufs.forEach(function(buffer) {
        buffer.copy(body, i, 0, buffer.length);
        i += buffer.length;
      });

      console.log('read end');
      cb(body);
    });

    stream.on('error', function(err) {
      console.log('Error:', err);
    });*/
  } else {
    cb();
  }
};

Medea.prototype.remove = function(key, cb) {
  var that = this;
  this.put(key, tombstone, function() {
    if (that.keydir[key]) {
      delete that.keydir[key];
    }

    cb();
  });
};

Medea.prototype.sync = function(cb) {
  fs.fsync(this.active.fd, cb);
};
<commit_msg>Adding lock acquisition and write lock file.<commit_after>var fs = require('fs');
var crc32 = require('crc32');
var msgpack = require('msgpack-js');

var sizes = {
  timestamp: 32,
  keysize: 16,
  valsize: 32,
  crc: 32,
  header: 32 + 32 + 16 + 32 // crc + timestamp + keysize + valsize
};

var tombstone = 'medea_tombstone';

var KeyDirEntry = function() {
  this.fileId = null;
  this.valueSize = null;
  this.valuePosition = null;
  this.timestamp = null;
};

var FileInfo = function() {
  this.filename = null;
  this.fd = null;
};

var Medea = module.exports = function(options) {
  this.active = null;
  this.keydir = {};

  options = options || {};

  this.maxFileSize = options.hasOwnProperty('maxFileSize')
    ? options.maxFileSize : 2147483648; //2GB

  this.mergeWindow = options.hasOwnProperty('mergeWindow')
    ? options.mergeWindow : 'always';

  this.fragMergeTrigger = options.hasOwnProperty('fragMergeTrigger')
    ? options.fragMergeTrigger : 60; // fragmentation >= 60%

  this.deadBytesMergeTrigger = options.hasOwnProperty('deadBytesMergeTrigger')
    ? options.deadBytesMergeTrigger : 536870912; // dead bytes > 512MB

  this.fragThreshold = options.hasOwnProperty('fragThreshold')
    ? options.fragThreshold : 40; // fragmentation >= 40%

  this.deadBytesThreshold = options.hasOwnProperty('deadBytesThreshold')
    ? options.deadBytesThreshold : 134217728; // dead bytes > 128MB

  this.smallFileThreshold = options.hasOwnProperty('smallFileThreshold')
    ? options.smallFileThreshold : 10485760 // file < 10MB

  this.maxFoldAge = options.hasOwnProperty('maxFoldAge') ? options.maxFoldAge : -1;
  this.maxFoldPuts = options.hasOwnProperty('maxFoldPuts') ? options.maxFoldPuts : 0;
  this.expirySecs = options.hasOwnProperty('expirySecs') ? options.expirySecs : -1;
};

Medea.prototype.open = function(dir, options, cb) {
  if (typeof options === 'function') {
    cb = options;
    options = {};
  }

  if (typeof dir === 'function') {
    options = {};
    cb = dir;
    dir = 'medea';
  }

  this.readOnly = options.hasOwnProperty('readOnly') ? options.readOnly : false;
  this.active = new FileInfo();
  this.active.filename =  dir + '/' + Date.now().toString() + '.medea.data';

  var that = this;

  dir = __dirname + '/' + dir;
  var next = function(dir, readOnly, cb) {
    if (!readOnly) {
      that._acquire(dir, 'write', function(err, val) {
        if (err) {
          cb(err);
          return;
        }
        that._open(dir, cb);
      });
    } else {
      that._open(dir, cb);
    }
  }

  fs.stat(dir, function(err, stat) {
    if (!stat) {
      fs.mkdir(dir, function(err) {
        if (err) {
          cb(err);
          return;
        }
        next(dir, that.readOnly, cb);
      });
    } else {
      next(dir, that.readOnly, cb);
    }
  });
};

Medea.prototype._acquire = function(dir, type, cb) {
  var filename = dir + '/medea.' + type + '.lock';

  var that = this;
  var writeFile = function(obj) {
    that._acquireLock(filename, true, function(err, fd) {
      var stream = fs.createWriteStream(filename, { fd: fd });
      stream.write(process.pid + ' \n');
      stream.on('close', function() {
        fs.close(fd, function() {
          cb(null, obj);
        });
      });
      stream.end();
    });
  };

  var bufs = [];
  var len = 0;
  var ondata = function(data) {
    bufs.push(data);
    len += data.length;
  };

  var onend = function(fd) {
    return function() {
      var contents = new Buffer(len);
      var i = 0;
      bufs.forEach(function(buf) {
        buf.copy(contents, i, 0, buf.length);
        i += buf.length;
      });

      var c = contents.toString().split(' ');

      if (c.length && c[0].length) {
        var pid = c[0];
        var fname;
        if (c[1] && c[1].length && c[1] !== '\n') {
          fname = c[1];
        }

        var ret = { pid: pid, filename: fname };

        if (pid === process.pid) {
          cb(null, ret);
        } else {
          fs.unlink(filename, function(err) {
            if (err) {
              cb(err);
              return;
            }
            writeFile(ret);
          });
        }
      } else {
        writeFile(ret);
      }
    };
  };

  this._acquireLock(filename, false, function(err, fd) {
    if (err) {
      if (err.code === 'ENOENT') {
        // this is okay.  move on.
        writeFile({ pid: process.pid, filename: null });
        return;
      }
      cb(err);
      return;
    }
    var stream = fs.createReadStream(filename, { fd: fd });
    stream.on('data', ondata);
    stream.on('end', onend(fd));

    stream.resume();
  });

  /*fs.stat(filename, function(err, stats) {
    if (stats) {
      var stream = fs.createReadStream(filename);
      stream.on('open', function() {
        stream.on('data', ondata);
        stream.on('end', onend);
      });

      stream.resume();
    } else {
      writeFile({ pid: process.pid, filename: null });
    }
  });*/
};

Medea.prototype._acquireLock = function(filename, isWriteLock, cb) {
  var fsflags = {
    O_RDONLY: 0x0,
    O_CREAT: 0x100,
    O_EXCL: 0x200,
    O_RDWR: 0x02,
    O_SYNC: 0x1000
  };
  var writeLockFlags = fsflags.O_CREAT | fsflags.O_EXCL | fsflags.O_RDWR | fsflags.O_SYNC;

  var flags = isWriteLock ? writeLockFlags : fsflags.O_RDONLY;

  fs.open(filename, flags, 0600, cb);
};

Medea.prototype._open = function(dir, cb) {
  var file = this.active;
  fs.open(this.active.filename, 'a+', function(err, value) {
    file.fd = value;
    cb();
  });
};

Medea.prototype.close = function(cb) {
  if (this.active.fd) {
    fs.close(this.active.fd, cb);
  } else {
    cb();
  }
};

Medea.prototype.put = function(k, v, cb) {
  var that = this;
  fs.stat(this.active.filename, function(err, stat) {
    var offset = stat ? stat.size : 0;
    that._put(k, v, offset, cb);
  });
};

Medea.prototype._put = function(k, v, offset, cb) {
  var packed = msgpack.encode(v);

  var ts = Date.now();

  var crc = new Buffer(sizes.crc);
  var timestamp = new Buffer(sizes.timestamp);
  var keysz = new Buffer(sizes.keysize);
  var valuesz = new Buffer(sizes.valsize);
  var key = new Buffer(k);
  var value = new Buffer(packed);

  timestamp.write(ts.toString());
  keysz.write(key.length.toString());
  valuesz.write(value.length.toString());

  var bufs = Buffer.concat([timestamp, keysz, valuesz, key, value]);
  crc.write(crc32(bufs));

  var line = Buffer.concat([crc, bufs]);

  this._write(line, offset);

  var entry = new KeyDirEntry();
  entry.fileId = this._fileTimestamp(this.active.filename);
  entry.valueSize = value.length;
  entry.valuePosition = offset + sizes.header + key.length;
  entry.timestamp = ts;

  this.keydir[k] = entry;

  cb();
};

Medea.prototype._fileTimestamp = function(file) {
  var f = file.replace('\\', '/');
  var lastSlashLocation = file.lastIndexOf('/');
  var dotLocation = file.indexOf('.');
  var ts = file.substring(lastSlashLocation+1, dotLocation);
  return Number(ts);
};

Medea.prototype._write = function(bufs, offset) {
  var stream = fs.createWriteStream(this.active.filename, { flags: 'a', fd: this.active.fd });
  stream.write(bufs);
};

Medea.prototype.get = function(key, cb) {
  var entry = this.keydir[key];
  if (entry) {
    var readBuffer = new Buffer(entry.valueSize);
    fs.read(this.active.fd, readBuffer, 0, entry.valueSize, entry.valuePosition, function(err, bytesRead, buffer) {
      var msg = msgpack.decode(buffer);
      if (msg !== tombstone) {
        cb(msg);
      } else {
        cb();
      }
    });

    // Not working properly on node v0.8.x
    /*var options = {
      flags: 'r',
      autoClose: false,
      start: entry.valuePosition,
      end: entry.valuePosition + entry.valueSize,
      fd: this.active.fd
    }

    var stream = fs.createReadStream(this.active.filename, options);
    var bufs = [];
    var len = 0;

    stream.on('data', function(data) {
      len += data.length;
      bufs.push(data);
    });

    stream.on('end', function(data) {
      var i = 0;
      var body = new Buffer(len);
      bufs.forEach(function(buffer) {
        buffer.copy(body, i, 0, buffer.length);
        i += buffer.length;
      });

      console.log('read end');
      cb(body);
    });

    stream.on('error', function(err) {
      console.log('Error:', err);
    });*/
  } else {
    cb();
  }
};

Medea.prototype.remove = function(key, cb) {
  var that = this;
  this.put(key, tombstone, function() {
    if (that.keydir[key]) {
      delete that.keydir[key];
    }

    cb();
  });
};

Medea.prototype.sync = function(cb) {
  fs.fsync(this.active.fd, cb);
};
<|endoftext|>


<commit_before>var passport = require('passport');
var bcrypt = require('bcrypt-nodejs');
var Model = require('./model');
var bodyParser = require('body-parser');

var getConnection  = require('./connectionpool');

var sessionIdArr = [];

//-------------------------------------------------------

var home = function(req, res, next) {
  if (!req.isAuthenticated()) {
    res.redirect('/signin');
  } else {
      console.log("on home page")
      var sessionId = req.params.sessionId;
      sessionIdArr.push(sessionId);

      var user = req.user;
      var arr = [];
      var arr2 = [];

      if (user !== undefined) {
        user = user.toJSON();
      }

    getConnection(function (err, connection) {
      connection.query("SELECT * FROM history WHERE history.sessionId =" + '"' + sessionId + '"',
        function(err, rows) {

        var obj = rows[0];

        for (var prop in obj) {
          if (obj[prop] == "1") {
          arr.push("yes");
          } else {
          arr.push("no")
          }
        }

        arr.splice(0, 8);
        res.render('home', {
          title: 'Home',
          user: user,
          arr: arr
        });
        connection.release();
        });
    });
  }
};

//-------------------------------------------------------
var homePost = function(req, res, next) {

  var sessionId = sessionIdArr;
  var user = req.user;

  getConnection(function (err, connection) {
    connection.query('INSERT INTO history_review(username, userId, sessionId, followUpBrainInjury, followUpBrainInjuryNotes, followUpEpilepsy, followUpEpilepsyNotes, followUpEye, followUpEyeNotes, followUpEar, followUpEarNotes, followUpHeart, followUpHeartNotes, followUpPacemaker, followUpPacemakerNotes, followupBloodPressure, followupBloodPressureNotes, followUpHighCholesterol, followUpHighCholesterolNotes, followUpBreathingProblems, followUpBreathingProblemsNotes, followUpLungDisease, followUpLungDiseaseNotes, followUpBackProblems, followUpBackProblemsNotes, followUpKidneyProblems, followUpKidneyProblemsNotes, followUpStomachProblems, followUpStomachProblemsNotes, followUpDiabetes, followUpDiabetesNotes, followUpInsulin, followUpInsulinNotes, followUpAnxiety, followUpAnxietyNotes, followUpFainting, followUpFaintingNotes, followUpDizziness, followUpDizzinessNotes, followUpUnExplainedWeightLoss, followUpUnExplainedWeightLossNotes, followUpStroke, followUpStrokeNotes, followUpMissingLimbs, followUpMissingLimbsNotes, followUpBoneProblems, followUpBoneProblemsNotes, followUpBloodClots,followUpBloodClotsNotes, followUpCancer, followUpCancerNotes, followUpChronicDiseases, followUpChronicDiseasesNotes, followUpSleepDisorders, followUpSleepDisordersNotes, followUpSleepTest, followUpSleepTestNotes, followUpNightInHospital, followUpNightInHospitalNotes, followUpBrokenBone, followUpBrokenBoneNotes, followUpUseTobacco, followUpUseTobaccoNotes, followUpDrinkAlcohol,followUpDrinkAlcoholNotes, followUpIllegalSubstance, followUpIllegalSubstanceNotes, followUpFailedDrugTest, followUpFailedDrugTestNotes, historyReview) VALUES(' +
      "'" +
      user.attributes.username +
      "'," + "'" +
      user.attributes.userId +
      "'," + "'" +
      sessionId +
      "'," + "'" +
      req.body.followUpBrainInjury +
      "'," + "'" +
      req.body.followUpBrainInjuryNotes +
      "'," + "'" +
      req.body.followUpEpilepsy +
      "'," + "'" +
      req.body.followUpEpilepsyNotes +
      "'," + "'" +
      req.body.followUpEye +
      "'," + "'" +
      req.body.followUpEyeNotes +
      "'," + "'" +
      req.body.followUpEar +
      "'," + "'" +
      req.body.followUpEarNotes +
      "'," + "'" +
      req.body.followUpHeart +
      "'," + "'" +
      req.body.followUpHeartNotes +
      "'," + "'" +
      req.body.followUpPacemaker +
      "'," + "'" +
      req.body.followUpPacemakerNotes +
      "'," + "'" +
      req.body.followupBloodPressure +
      "'," + "'" +
      req.body.followupBloodPressureNotes +
      "'," + "'" +
      req.body.followUpHighCholesterol +
      "'," + "'" +
      req.body.followUpHighCholesterolNotes +
      "'," + "'" +
      req.body.followUpBreathingProblems +
      "'," + "'" +
      req.body.followUpBreathingProblemsNotes +
      "'," + "'" +
      req.body.followUpLungDisease +
      "'," + "'" +
      req.body.followUpLungDiseaseNotes +
      "'," + "'" +
      req.body.followUpBackProblems +
      "'," + "'" +
      req.body.followUpBackProblemsNotes +
      "'," + "'" +
      req.body.followUpKidneyProblems +
      "'," + "'" +
      req.body.followUpKidneyProblemsNotes +
      "'," + "'" +
      req.body.followUpStomachProblems +
      "'," + "'" +
      req.body.followUpStomachProblemsNotes +
      "'," + "'" +
      req.body.followUpDiabetes +
      "'," + "'" +
      req.body.followUpDiabetesNotes +
      "'," + "'" +
      req.body.followUpInsulin +
      "'," + "'" +
      req.body.followUpInsulinNotes +
      "'," + "'" +
      req.body.followUpAnxiety +
      "'," + "'" +
      req.body.followUpAnxietyNotes +
      "'," + "'" +
      req.body.followUpFainting +
      "'," + "'" +
      req.body.followUpFaintingNotes +
      "'," + "'" +
      req.body.followUpDizziness +
      "'," + "'" +
      req.body.followUpDizzinessNotes +
      "'," + "'" +
      req.body.followUpUnExplainedWeightLoss +
      "'," + "'" +
      req.body.followUpUnExplainedWeightLossNotes +
      "'," + "'" +
      req.body.followUpStroke +
      "'," + "'" +
      req.body.followUpStrokeNotes +
      "'," + "'" +
      req.body.followUpMissingLimbs +
      "'," + "'" +
      req.body.followUpMissingLimbsNotes +
      "'," + "'" +
      req.body.followUpBoneProblems +
      "'," + "'" +
      req.body.followUpBoneProblemsNotes +
      "'," + "'" +
      req.body.followUpBloodClots +
      "'," + "'" +
      req.body.followUpBloodClotsNotes +
      "'," + "'" +
      req.body.followUpCancer +
      "'," + "'" +
      req.body.followUpCancerNotes +
      "'," + "'" +
      req.body.followUpChronicDiseases +
      "'," + "'" +
      req.body.followUpChronicDiseasesNotes +
      "'," + "'" +
      req.body.followUpSleepDisorders +
      "'," + "'" +
      req.body.followUpSleepDisordersNotes +
      "'," + "'" +
      req.body.followUpSleepTest +
      "'," + "'" +
      req.body.followUpSleepTestNotes +
      "'," + "'" +
      req.body.followUpNightInHospital +
      "'," + "'" +
      req.body.followUpNightInHospitalNotes +
      "'," + "'" +
      req.body.followUpBrokenBone +
      "'," + "'" +
      req.body.followUpBrokenBoneNotes +
      "'," + "'" +
      req.body.followUpUseTobacco +
      "'," + "'" +
      req.body.followUpUseTobaccoNotes +
      "'," + "'" +
      req.body.followUpDrinkAlcohol +
      "'," + "'" +
      req.body.followUpDrinkAlcoholNotes +
      "'," + "'" +
      req.body.followUpIllegalSubstance +
      "'," + "'" +
      req.body.followUpIllegalSubstanceNotes +
      "'," + "'" +
      req.body.followUpFailedDrugTest +
      "'," + "'" +
      req.body.followUpFailedDrugTestNotes +
      "'," + "'" +
      req.body.historyReview +
      "')",

    function(err, rows) {
      connection.release();
    });
  });

  res.redirect('/landing/' + sessionId)
};

module.exports.home = home;
module.exports.homePost = homePost;
<commit_msg>added not sure to conditional apperance<commit_after>var passport = require('passport');
var bcrypt = require('bcrypt-nodejs');
var Model = require('./model');
var bodyParser = require('body-parser');

var getConnection  = require('./connectionpool');

var sessionIdArr = [];

//-------------------------------------------------------

var home = function(req, res, next) {
  if (!req.isAuthenticated()) {
    res.redirect('/signin');
  } else {
      console.log("on home page")
      var sessionId = req.params.sessionId;
      sessionIdArr.push(sessionId);

      var user = req.user;
      var arr = [];
      var arr2 = [];

      if (user !== undefined) {
        user = user.toJSON();
      }

    getConnection(function (err, connection) {
      connection.query("SELECT * FROM history WHERE history.sessionId =" + '"' + sessionId + '"',
        function(err, rows) {

        var obj = rows[0];

        for (var prop in obj) {
          if (obj[prop] == "1" || obj[prop] == "3" ) {
          arr.push("yes");
          } else {
          arr.push("no")
          }
        }

        arr.splice(0, 8);
        res.render('home', {
          title: 'Home',
          user: user,
          arr: arr
        });
        connection.release();
        });
    });
  }
};

//-------------------------------------------------------
var homePost = function(req, res, next) {

  var sessionId = sessionIdArr;
  var user = req.user;

  getConnection(function (err, connection) {
    connection.query('INSERT INTO history_review(username, userId, sessionId, followUpBrainInjury, followUpBrainInjuryNotes, followUpEpilepsy, followUpEpilepsyNotes, followUpEye, followUpEyeNotes, followUpEar, followUpEarNotes, followUpHeart, followUpHeartNotes, followUpPacemaker, followUpPacemakerNotes, followupBloodPressure, followupBloodPressureNotes, followUpHighCholesterol, followUpHighCholesterolNotes, followUpBreathingProblems, followUpBreathingProblemsNotes, followUpLungDisease, followUpLungDiseaseNotes, followUpBackProblems, followUpBackProblemsNotes, followUpKidneyProblems, followUpKidneyProblemsNotes, followUpStomachProblems, followUpStomachProblemsNotes, followUpDiabetes, followUpDiabetesNotes, followUpInsulin, followUpInsulinNotes, followUpAnxiety, followUpAnxietyNotes, followUpFainting, followUpFaintingNotes, followUpDizziness, followUpDizzinessNotes, followUpUnExplainedWeightLoss, followUpUnExplainedWeightLossNotes, followUpStroke, followUpStrokeNotes, followUpMissingLimbs, followUpMissingLimbsNotes, followUpBoneProblems, followUpBoneProblemsNotes, followUpBloodClots,followUpBloodClotsNotes, followUpCancer, followUpCancerNotes, followUpChronicDiseases, followUpChronicDiseasesNotes, followUpSleepDisorders, followUpSleepDisordersNotes, followUpSleepTest, followUpSleepTestNotes, followUpNightInHospital, followUpNightInHospitalNotes, followUpBrokenBone, followUpBrokenBoneNotes, followUpUseTobacco, followUpUseTobaccoNotes, followUpDrinkAlcohol,followUpDrinkAlcoholNotes, followUpIllegalSubstance, followUpIllegalSubstanceNotes, followUpFailedDrugTest, followUpFailedDrugTestNotes, historyReview) VALUES(' +
      "'" +
      user.attributes.username +
      "'," + "'" +
      user.attributes.userId +
      "'," + "'" +
      sessionId +
      "'," + "'" +
      req.body.followUpBrainInjury +
      "'," + "'" +
      req.body.followUpBrainInjuryNotes +
      "'," + "'" +
      req.body.followUpEpilepsy +
      "'," + "'" +
      req.body.followUpEpilepsyNotes +
      "'," + "'" +
      req.body.followUpEye +
      "'," + "'" +
      req.body.followUpEyeNotes +
      "'," + "'" +
      req.body.followUpEar +
      "'," + "'" +
      req.body.followUpEarNotes +
      "'," + "'" +
      req.body.followUpHeart +
      "'," + "'" +
      req.body.followUpHeartNotes +
      "'," + "'" +
      req.body.followUpPacemaker +
      "'," + "'" +
      req.body.followUpPacemakerNotes +
      "'," + "'" +
      req.body.followupBloodPressure +
      "'," + "'" +
      req.body.followupBloodPressureNotes +
      "'," + "'" +
      req.body.followUpHighCholesterol +
      "'," + "'" +
      req.body.followUpHighCholesterolNotes +
      "'," + "'" +
      req.body.followUpBreathingProblems +
      "'," + "'" +
      req.body.followUpBreathingProblemsNotes +
      "'," + "'" +
      req.body.followUpLungDisease +
      "'," + "'" +
      req.body.followUpLungDiseaseNotes +
      "'," + "'" +
      req.body.followUpBackProblems +
      "'," + "'" +
      req.body.followUpBackProblemsNotes +
      "'," + "'" +
      req.body.followUpKidneyProblems +
      "'," + "'" +
      req.body.followUpKidneyProblemsNotes +
      "'," + "'" +
      req.body.followUpStomachProblems +
      "'," + "'" +
      req.body.followUpStomachProblemsNotes +
      "'," + "'" +
      req.body.followUpDiabetes +
      "'," + "'" +
      req.body.followUpDiabetesNotes +
      "'," + "'" +
      req.body.followUpInsulin +
      "'," + "'" +
      req.body.followUpInsulinNotes +
      "'," + "'" +
      req.body.followUpAnxiety +
      "'," + "'" +
      req.body.followUpAnxietyNotes +
      "'," + "'" +
      req.body.followUpFainting +
      "'," + "'" +
      req.body.followUpFaintingNotes +
      "'," + "'" +
      req.body.followUpDizziness +
      "'," + "'" +
      req.body.followUpDizzinessNotes +
      "'," + "'" +
      req.body.followUpUnExplainedWeightLoss +
      "'," + "'" +
      req.body.followUpUnExplainedWeightLossNotes +
      "'," + "'" +
      req.body.followUpStroke +
      "'," + "'" +
      req.body.followUpStrokeNotes +
      "'," + "'" +
      req.body.followUpMissingLimbs +
      "'," + "'" +
      req.body.followUpMissingLimbsNotes +
      "'," + "'" +
      req.body.followUpBoneProblems +
      "'," + "'" +
      req.body.followUpBoneProblemsNotes +
      "'," + "'" +
      req.body.followUpBloodClots +
      "'," + "'" +
      req.body.followUpBloodClotsNotes +
      "'," + "'" +
      req.body.followUpCancer +
      "'," + "'" +
      req.body.followUpCancerNotes +
      "'," + "'" +
      req.body.followUpChronicDiseases +
      "'," + "'" +
      req.body.followUpChronicDiseasesNotes +
      "'," + "'" +
      req.body.followUpSleepDisorders +
      "'," + "'" +
      req.body.followUpSleepDisordersNotes +
      "'," + "'" +
      req.body.followUpSleepTest +
      "'," + "'" +
      req.body.followUpSleepTestNotes +
      "'," + "'" +
      req.body.followUpNightInHospital +
      "'," + "'" +
      req.body.followUpNightInHospitalNotes +
      "'," + "'" +
      req.body.followUpBrokenBone +
      "'," + "'" +
      req.body.followUpBrokenBoneNotes +
      "'," + "'" +
      req.body.followUpUseTobacco +
      "'," + "'" +
      req.body.followUpUseTobaccoNotes +
      "'," + "'" +
      req.body.followUpDrinkAlcohol +
      "'," + "'" +
      req.body.followUpDrinkAlcoholNotes +
      "'," + "'" +
      req.body.followUpIllegalSubstance +
      "'," + "'" +
      req.body.followUpIllegalSubstanceNotes +
      "'," + "'" +
      req.body.followUpFailedDrugTest +
      "'," + "'" +
      req.body.followUpFailedDrugTestNotes +
      "'," + "'" +
      req.body.historyReview +
      "')",

    function(err, rows) {
      connection.release();
    });
  });

  res.redirect('/landing/' + sessionId)
};

module.exports.home = home;
module.exports.homePost = homePost;
<|endoftext|>


<commit_before>var rethinkdbdash = require('rethinkdbdash');
var Promise = require('bluebird');
var Model = require(__dirname+'/model.js');
var util = require(__dirname+'/util.js');
var type = require(__dirname+'/type/index.js');
var Query = require(__dirname+'/query.js');
var Errors = require(__dirname+'/errors.js');

/**
 * Main method, create the default database.
 *
 * @param {Object} options the options for the driver and the future models created.
 *  - `max` {number} The maximum number of connections in the pool, default 1000
 *  - `buffer` {number} The minimum number of connections available in the pool, default 50
 *  - `timeoutError` {number} The wait time before reconnecting in case of an error (in ms), default 1000
 *  - `timeoutGb` {number} How long the pool keep a connection that hasn't been used (in ms), default 60*60*1000
 *  - `enforce_missing` {boolean}, default `false`
 *  - `enforce_extra` {"strict"|"remove"|"none"}, default `"none"`
 *  - `enforce_type` {"strict"|"loose"|"none"}, default `"loose"`
 *  - `timeFormat` {"raw"|"native"}
 */
function Thinky(config) {
  var self = this;

  config = config || {};
  config.db = config.db || 'test'; // We need the default db to create it.
  self._config = config;

  self._options = {};
  // Option passed to each model we are going to create.
  self._options.enforce_missing =
    (config.enforce_missing != null) ? config.enforce_missing : false;
  self._options.enforce_extra =
    (config.enforce_extra != null) ? config.enforce_extra : "none";
  self._options.enforce_type =
    (config.enforce_type != null) ? config.enforce_type : 'loose';

  // Format of time objects returned by the database, by default we convert
  // them to JavaScript Dates.
  self._options.timeFormat =
    (config.timeFormat != null) ? config.timeFormat : 'native';
  // Option passed to each model we are going to create.
  self._options.validate =
    (config.validate != null) ? config.validate : 'onsave';

  self.r = rethinkdbdash(config);
  self.type = type;
  self.Query = Query;
  self.models = {};

  // Export errors
  self.Errors = Errors;

  // Initialize the database.
  self.dbReady().then().error(function(error) {
    throw error;
  });
}

/**
 * Initialize our database.
 * @return {Promise=} Returns a promise which will resolve when the database is ready.
 */
Thinky.prototype.dbReady = function() {
  var self = this;
  if (this._dbReadyPromise) return this._dbReadyPromise;
  var r = self.r;
  this._dbReadyPromise = r.dbCreate(self._config.db)
  .run()
  .error(function(error) {
    // The `do` is not atomic, we a concurrent query could create the database
    // between the time `dbList` is ran and `dbCreate` is.
    if (error.message.match(/^Database `.*` already exists in/)) {
      return;
    }

    // In case something went wrong here, we do not recover and throw.
    throw error;
  });

  return self._dbReadyPromise;
};

/**
 * Return the current option used.
 * @return {object} The global options of the library
 */
Thinky.prototype.getOptions = function() {
  return this._options;
}

/**
 * Create a model
 *
 * @param {string} name The name of the table used behind this model.
 * @param {object|Type} schema The schema of this model.
 * @param {object=} options Options for this model. The fields can be:
 *  - `init` {boolean} Whether the table should be created or not. The value
 *  `false` is used to speed up testing, and should probably be `true` in
 *  other use cases.
 *  - `timeFormat` {"raw"|"native"} Format of ReQL dates.
 *  - `enforce_missing` {boolean}, default `false`.
 *  - `enforce_extra` {"strict"|"remove"|"none"}, default `"none"`.
 *  - `enforce_type` {"strict"|"loose"|"none"}, default `"loose"`.
 *  - `validate` {"oncreate"|"onsave"}, default "onsave".
 */
Thinky.prototype.createModel = function(name, schema, options) {
  var self = this;

  // Make a deep copy of the options as the model may overwrite them.
  var fullOptions = util.deepCopy(this._options);
  options = options || {};
  util.loopKeys(options, function(options, key) {
    fullOptions[key] = options[key];
  });

  // Two models cannot share the same name.
  if (self.models[name] !== undefined) {
    throw new Error("Cannot redefine a Model");
  }

  // Create the constructor returned. This will also validate the schema.
  var model = Model.new(name, schema, fullOptions, self);

  // Keep a reference of this model.
  self.models[name] = model;
  return model;
}

/**
 * Method to clean all the references to the models. This is used to speed up
 * testing and should not be used in other use cases.
 */
Thinky.prototype._clean = function() {
  this.models = {};
}

// Export the module.
module.exports = function(config) {
  return new Thinky(config);
}

// Expose thinky types directly from module
module.exports.type = type;
<commit_msg>Let users provide an instance of rethinkdbdash<commit_after>var rethinkdbdash = require('rethinkdbdash');
var Promise = require('bluebird');
var Model = require(__dirname+'/model.js');
var util = require(__dirname+'/util.js');
var type = require(__dirname+'/type/index.js');
var Query = require(__dirname+'/query.js');
var Errors = require(__dirname+'/errors.js');

/**
 * Main method, create the default database.
 *
 * @param {Object} options the options for the driver and the future models created.
 *  - `max` {number} The maximum number of connections in the pool, default 1000
 *  - `buffer` {number} The minimum number of connections available in the pool, default 50
 *  - `timeoutError` {number} The wait time before reconnecting in case of an error (in ms), default 1000
 *  - `timeoutGb` {number} How long the pool keep a connection that hasn't been used (in ms), default 60*60*1000
 *  - `enforce_missing` {boolean}, default `false`
 *  - `enforce_extra` {"strict"|"remove"|"none"}, default `"none"`
 *  - `enforce_type` {"strict"|"loose"|"none"}, default `"loose"`
 *  - `timeFormat` {"raw"|"native"}
 */
function Thinky(config) {
  var self = this;

  config = config || {};
  config.db = config.db || 'test'; // We need the default db to create it.
  self._config = config;

  self._options = {};
  // Option passed to each model we are going to create.
  self._options.enforce_missing =
    (config.enforce_missing != null) ? config.enforce_missing : false;
  self._options.enforce_extra =
    (config.enforce_extra != null) ? config.enforce_extra : "none";
  self._options.enforce_type =
    (config.enforce_type != null) ? config.enforce_type : 'loose';

  // Format of time objects returned by the database, by default we convert
  // them to JavaScript Dates.
  self._options.timeFormat =
    (config.timeFormat != null) ? config.timeFormat : 'native';
  // Option passed to each model we are going to create.
  self._options.validate =
    (config.validate != null) ? config.validate : 'onsave';

  if (config.r === undefined) {
    self.r = rethinkdbdash(config);
  }
  else {
    self.r = config.r;
  }
  self.type = type;
  self.Query = Query;
  self.models = {};

  // Export errors
  self.Errors = Errors;

  // Initialize the database.
  self.dbReady().then().error(function(error) {
    throw error;
  });
}

/**
 * Initialize our database.
 * @return {Promise=} Returns a promise which will resolve when the database is ready.
 */
Thinky.prototype.dbReady = function() {
  var self = this;
  if (this._dbReadyPromise) return this._dbReadyPromise;
  var r = self.r;
  this._dbReadyPromise = r.dbCreate(self._config.db)
  .run()
  .error(function(error) {
    // The `do` is not atomic, we a concurrent query could create the database
    // between the time `dbList` is ran and `dbCreate` is.
    if (error.message.match(/^Database `.*` already exists in/)) {
      return;
    }

    // In case something went wrong here, we do not recover and throw.
    throw error;
  });

  return self._dbReadyPromise;
};

/**
 * Return the current option used.
 * @return {object} The global options of the library
 */
Thinky.prototype.getOptions = function() {
  return this._options;
}

/**
 * Create a model
 *
 * @param {string} name The name of the table used behind this model.
 * @param {object|Type} schema The schema of this model.
 * @param {object=} options Options for this model. The fields can be:
 *  - `init` {boolean} Whether the table should be created or not. The value
 *  `false` is used to speed up testing, and should probably be `true` in
 *  other use cases.
 *  - `timeFormat` {"raw"|"native"} Format of ReQL dates.
 *  - `enforce_missing` {boolean}, default `false`.
 *  - `enforce_extra` {"strict"|"remove"|"none"}, default `"none"`.
 *  - `enforce_type` {"strict"|"loose"|"none"}, default `"loose"`.
 *  - `validate` {"oncreate"|"onsave"}, default "onsave".
 */
Thinky.prototype.createModel = function(name, schema, options) {
  var self = this;

  // Make a deep copy of the options as the model may overwrite them.
  var fullOptions = util.deepCopy(this._options);
  options = options || {};
  util.loopKeys(options, function(options, key) {
    fullOptions[key] = options[key];
  });

  // Two models cannot share the same name.
  if (self.models[name] !== undefined) {
    throw new Error("Cannot redefine a Model");
  }

  // Create the constructor returned. This will also validate the schema.
  var model = Model.new(name, schema, fullOptions, self);

  // Keep a reference of this model.
  self.models[name] = model;
  return model;
}

/**
 * Method to clean all the references to the models. This is used to speed up
 * testing and should not be used in other use cases.
 */
Thinky.prototype._clean = function() {
  this.models = {};
}

// Export the module.
module.exports = function(config) {
  return new Thinky(config);
}

// Expose thinky types directly from module
module.exports.type = type;
<|endoftext|>


<commit_before>'use strict';

const USERS = require('./users.js');
const DB = require('./database.js');

// Need to figure out a way to create individual cancelable timers for each
// channel in the database.

// I think creating an object and storing all the channels in the object at
// startup is the best way to do it.
var timers = {};

/*
The object could look something like this:

{
  #talutha: {
    offlineInterval: 20,
    onlineInterval: 5,
    timer: setInterval(addPointsOnTimerFunction, seeIfChannelOnlineAndSetTimeAccordingly)
  }
}

Then maybe we start the timer function when the channel starts. If a channel
goes online during the timer, we would need to cancel it and set a new timer.
I think an overall check should be done for every channel would be the easiest.

*/

function addProp(channel, innerProps) {
  timers[channel] = innerProps;
}

function addChannel(channel) {
  timers[channel] = {
    channelName: channel,
    pointsTimer: false,
    online: false,
    startTimer: function(time) {
      this.pointsTimer = setInterval( function() {
        iteratePoints(channel);
      }
        , time);
    },
    stopTimer: function() {
      clearInterval(this.pointsTimer);
      this.pointsTimer = false;
    },
    chickenDinner: {
      activated: true,
      cooldown: false,
      state: 'available',
      pot: {
        total: 0,
        first: 0,
        second: 0,
        third: 0
      },
      participants: [],
      timer: function(func, time) {
        setTimeout( func, time);
      }
    }
  };
}

function checkOnlineStatus(func) {
  setInterval( func, 10000);
}

async function iteratePoints(channel) {
  try {

    let db = await DB.db;
    console.log('Running Points Iteration for ' + channel);
    let channel_settings = await db.channel_settings.find({ channel: channel });
    let points = channel_settings[0].points_per_iteration;
    let userList = await USERS.getUserList(db, channel);
    await USERS.processForPoints(db, userList, channel, points);

  } catch (err) {
    console.log('Point Iteration Error:');
    console.log(err);
  }

}

async function startPointsTimer(db, channel) {
  try {

    if (Array.isArray(channel)) {
      channel = channel.join(',');
    }

    let interval;
    let channelSettings = await db.channel_settings.find({ channel: channel });
    let pointsActivated = channelSettings[0].points_activated;
    channelSettings = channelSettings[0];

    // check is channel has points enabled
    if (pointsActivated && !timers[channel].online) {

      interval = channelSettings.points_offline_interval;

    } else if (pointsActivated && timers[channel].online) {

      interval = channelSettings.points_online_interval;

    }

    // convert interval to seconds
    interval = parseInt(interval);
    interval = interval * 1000;
    timers[channel].startTimer(interval);

  } catch (err) {
    console.log('startPointsTimer Error:');
    console.log(err);
  }
}

function stopPointsTimer(channel) {

  timers[channel].stopTimer();

}

function changeOnlineStatus(channel) {
  let db = global.db;
  timers[channel].online = !timers[channel].online;
  stopPointsTimer(channel);
  startPointsTimer(db, channel);
}

function showTimers() {
  return timers;
}

function hourlyResets() {

  for (var channel in timers) {
    if (timers.hasOwnProperty(channel)) {
      timers[channel].chickenDinner.cooldown = false;
    }
  }
}

// resets certain things at the top of each hour. Polling every 45 seconds
// because I am paranoid and don't want to miss an hour.
function hourlyTimer() {
  let currentMinute = new Date().getMinutes();
  let reset = false;

  if (currentMinute === 0 && !reset) {
    hourlyResets();
    reset = true;
  } else if (currentMinute === 1) {
    reset = false;
  }

  setTimeout(hourlyTimer, 45000);
}

module.exports = {
  addProp: addProp,
  showTimers: showTimers,
  addChannel: addChannel,
  startPointsTimer: startPointsTimer,
  stopPointsTimer: stopPointsTimer,
  changeOnlineStatus: changeOnlineStatus,
  checkOnlineStatus: checkOnlineStatus,
  hourlyTimer: hourlyTimer
};
<commit_msg>remove points iteration console logging<commit_after>'use strict';

const USERS = require('./users.js');
const DB = require('./database.js');

// Need to figure out a way to create individual cancelable timers for each
// channel in the database.

// I think creating an object and storing all the channels in the object at
// startup is the best way to do it.
var timers = {};

/*
The object could look something like this:

{
  #talutha: {
    offlineInterval: 20,
    onlineInterval: 5,
    timer: setInterval(addPointsOnTimerFunction, seeIfChannelOnlineAndSetTimeAccordingly)
  }
}

Then maybe we start the timer function when the channel starts. If a channel
goes online during the timer, we would need to cancel it and set a new timer.
I think an overall check should be done for every channel would be the easiest.

*/

function addProp(channel, innerProps) {
  timers[channel] = innerProps;
}

function addChannel(channel) {
  timers[channel] = {
    channelName: channel,
    pointsTimer: false,
    online: false,
    startTimer: function(time) {
      this.pointsTimer = setInterval( function() {
        iteratePoints(channel);
      }
        , time);
    },
    stopTimer: function() {
      clearInterval(this.pointsTimer);
      this.pointsTimer = false;
    },
    chickenDinner: {
      activated: true,
      cooldown: false,
      state: 'available',
      pot: {
        total: 0,
        first: 0,
        second: 0,
        third: 0
      },
      participants: [],
      timer: function(func, time) {
        setTimeout( func, time);
      }
    }
  };
}

function checkOnlineStatus(func) {
  setInterval( func, 10000);
}

async function iteratePoints(channel) {
  try {

    let db = await DB.db;
    //console.log('Running Points Iteration for ' + channel);
    let channel_settings = await db.channel_settings.find({ channel: channel });
    let points = channel_settings[0].points_per_iteration;
    let userList = await USERS.getUserList(db, channel);
    await USERS.processForPoints(db, userList, channel, points);

  } catch (err) {
    console.log('Point Iteration Error:');
    console.log(err);
  }

}

async function startPointsTimer(db, channel) {
  try {

    if (Array.isArray(channel)) {
      channel = channel.join(',');
    }

    let interval;
    let channelSettings = await db.channel_settings.find({ channel: channel });
    let pointsActivated = channelSettings[0].points_activated;
    channelSettings = channelSettings[0];

    // check is channel has points enabled
    if (pointsActivated && !timers[channel].online) {

      interval = channelSettings.points_offline_interval;

    } else if (pointsActivated && timers[channel].online) {

      interval = channelSettings.points_online_interval;

    }

    // convert interval to seconds
    interval = parseInt(interval);
    interval = interval * 1000;
    timers[channel].startTimer(interval);

  } catch (err) {
    console.log('startPointsTimer Error:');
    console.log(err);
  }
}

function stopPointsTimer(channel) {

  timers[channel].stopTimer();

}

function changeOnlineStatus(channel) {
  let db = global.db;
  timers[channel].online = !timers[channel].online;
  stopPointsTimer(channel);
  startPointsTimer(db, channel);
}

function showTimers() {
  return timers;
}

function hourlyResets() {

  for (var channel in timers) {
    if (timers.hasOwnProperty(channel)) {
      timers[channel].chickenDinner.cooldown = false;
    }
  }
}

// resets certain things at the top of each hour. Polling every 45 seconds
// because I am paranoid and don't want to miss an hour.
function hourlyTimer() {
  let currentMinute = new Date().getMinutes();
  let reset = false;

  if (currentMinute === 0 && !reset) {
    hourlyResets();
    reset = true;
  } else if (currentMinute === 1) {
    reset = false;
  }

  setTimeout(hourlyTimer, 45000);
}

module.exports = {
  addProp: addProp,
  showTimers: showTimers,
  addChannel: addChannel,
  startPointsTimer: startPointsTimer,
  stopPointsTimer: stopPointsTimer,
  changeOnlineStatus: changeOnlineStatus,
  checkOnlineStatus: checkOnlineStatus,
  hourlyTimer: hourlyTimer
};
<|endoftext|>