<commit_before>/* global atob */
import Ember from 'ember';
import FileReader from './system/file-reader';
import HTTPRequest from './system/http-request';
import uuid from './system/uuid';

const { get, set, RSVP } = Ember;
const { reads } = Ember.computed;

function normalizeOptions(file, url, options) {
  if (typeof url === 'object') {
    options = url;
    url = null;
  }

  options = options || {};

  options.url = options.url || url;
  options.method = options.method || 'POST';
  options.accepts = options.accepts || ['application/json', 'text/javascript'];
  options.contentType = options.contentType || get(file, 'type');
  options.headers = options.headers || {};
  options.data = options.data || {};
  options.fileKey = options.fileKey || 'file';

  if (options.headers.Accept == null) {
    if (!Array.isArray(options.accepts)) {
      options.accepts = [options.accepts];
    }
    options.headers.Accept = options.accepts.join(',');
  }

  // Set Content-Type in the data payload
  // instead of the headers, since the header
  // for Content-Type will always be multipart/form-data
  if (options.contentType) {
    options.data['Content-Type'] = options.contentType;
  }

  options.data[options.fileKey] = file.blob;

  return options;
}

let inflightRequests = 0;
Ember.Test.registerWaiter(null, function () {
  return inflightRequests === 0;
});

export default Ember.Object.extend({

  init() {
    this._super();
    Object.defineProperty(this, 'id', {
      writeable: false,
      enumerable: true,
      value: `file-${uuid()}`
    });
  },

  /**
    A unique id generated for this file.

    @property
    @type {String}
    @readonly
   */
  id: null,

  /**
    The file name.

    @property name
    @type {String}
   */
  name: reads('blob.name'),

  /**
    The size of the file in bytes.

    @property size
    @type {Number}
    @readonly
   */
  size: reads('blob.size'),

  /**
    The MIME type of the file.

    For a image file this may be `image/png`.

    @property type
    @type {String}
    @readonly
   */
  type: reads('blob.type'),

  /**
    @property loaded
    @type {Number}
    @default 0
    @readonly
   */
  loaded: 0,

  /**
    @property progress
    @type {Number}
    @default 0
    @readonly
   */
  progress: 0,

  /**
    The current state that the file is in.
    One of:

    - `queued`
    - `uploading`
    - `timed_out`
    - `aborted`
    - `uploaded`
    - `failed`

    @property state
    @type {String}
    @default 'queued'
    @readonly
   */
  state: 'queued',

  upload(url, opts) {
    if (['queued', 'failed', 'timed_out'].indexOf(get(this, 'state')) === -1) {
      Ember.assert(`The file ${this.id} is in the state "${get(this, 'state')}" and cannot be requeued.`);
    }

    let options = normalizeOptions(this, url, opts);

    // Build the form
    let form = new FormData();

    Object.keys(options.data).forEach(function (key) {
      form.append(key, options.data[key]);
    });

    let request = new HTTPRequest();
    request.open(options.method, options.url);

    Object.keys(options.headers).forEach(function (key) {
      request.setRequestHeader(key, options.headers[key]);
    });

    if (options.timeout) {
      request.timeout = options.timeout;
    }

    request.onprogress = (evt) => {
      if (evt.lengthComputable) {
        set(this, 'loaded', evt.loaded);
        set(this, 'size', evt.total);
        set(this, 'progress', (evt.loaded / evt.total) * 100);
      }
    };

    request.ontimeout = () => {
      set(this, 'state', 'timed_out');
    };

    request.onabort = () => {
      set(this, 'state', 'aborted');
    };

    set(this, 'state', 'uploading');

    // Increment for Ember.Test
    inflightRequests++;

    return request.send(form).then((result) => {
      set(this, 'state', 'uploaded');
      return result;
    }, (error) => {
      set(this, 'state', 'failed');
      return RSVP.reject(error);
    }).finally(function () {
      // Decrement for Ember.Test
      inflightRequests--;
    });
  },

  read(options={ as: 'data-url' }) {
    let reader = new FileReader();

    let blob = this.blob;
    switch (options.as) {
    case 'array-buffer':
      return reader.readAsArrayBuffer(blob);
    case 'data-url':
      return reader.readAsDataURL(blob);
    case 'binary-string':
      return reader.readAsBinaryString(blob);
    case 'text':
      return reader.readAsText(blob);
    }
  }
}).reopenClass({

  /**
    Creates a file object that can be read or uploaded to a
    server from a Blob object.

    @method fromBlob
    @param {Blob} blob The blob to create the file from.
    @return {File} A file object
   */
  fromBlob(blob) {
    let file = this.create();
    Object.defineProperty(file, 'blob', {
      writeable: false,
      enumerable: false,
      value: blob
    });

    return file;
  },

  fromDataURL(dataURL) {
    let [typeInfo, base64String] = dataURL.split(',');
    let mimeType = typeInfo.match(/:(.*?);/)[1];

    let binaryString = atob(base64String);
    let binaryData = new Uint8Array(binaryString.length);

    for (let i = 0, len = binaryString.length; i < len; i++) {
      binaryData[i] = binaryString.charCodeAt(i);
    }

    let blob = new Blob([binaryData], { type: mimeType });

    return this.fromBlob(blob);
  }
});
<commit_msg>use the file's name property when uploading<commit_after>/* global atob */
import Ember from 'ember';
import FileReader from './system/file-reader';
import HTTPRequest from './system/http-request';
import uuid from './system/uuid';

const { get, set, RSVP } = Ember;
const { reads } = Ember.computed;

function normalizeOptions(file, url, options) {
  if (typeof url === 'object') {
    options = url;
    url = null;
  }

  options = options || {};

  options.url = options.url || url;
  options.method = options.method || 'POST';
  options.accepts = options.accepts || ['application/json', 'text/javascript'];
  options.contentType = options.contentType || get(file, 'type');
  options.headers = options.headers || {};
  options.data = options.data || {};
  options.fileKey = options.fileKey || 'file';

  if (options.headers.Accept == null) {
    if (!Array.isArray(options.accepts)) {
      options.accepts = [options.accepts];
    }
    options.headers.Accept = options.accepts.join(',');
  }

  // Set Content-Type in the data payload
  // instead of the headers, since the header
  // for Content-Type will always be multipart/form-data
  if (options.contentType) {
    options.data['Content-Type'] = options.contentType;
  }

  options.data[options.fileKey] = file.blob;

  return options;
}

let inflightRequests = 0;
Ember.Test.registerWaiter(null, function () {
  return inflightRequests === 0;
});

export default Ember.Object.extend({

  init() {
    this._super();
    Object.defineProperty(this, 'id', {
      writeable: false,
      enumerable: true,
      value: `file-${uuid()}`
    });
  },

  /**
    A unique id generated for this file.

    @property
    @type {String}
    @readonly
   */
  id: null,

  /**
    The file name.

    @property name
    @type {String}
   */
  name: reads('blob.name'),

  /**
    The size of the file in bytes.

    @property size
    @type {Number}
    @readonly
   */
  size: reads('blob.size'),

  /**
    The MIME type of the file.

    For a image file this may be `image/png`.

    @property type
    @type {String}
    @readonly
   */
  type: reads('blob.type'),

  /**
    Returns the appropriate file extension of
    the file according to the type

    @property extension
    @type {String}
    @readonly
   */
  extension: computed('type', {
    get() {
      return get(this, 'type').split('/').slice(-1)[0];
    }
  })

  /**
    @property loaded
    @type {Number}
    @default 0
    @readonly
   */
  loaded: 0,

  /**
    @property progress
    @type {Number}
    @default 0
    @readonly
   */
  progress: 0,

  /**
    The current state that the file is in.
    One of:

    - `queued`
    - `uploading`
    - `timed_out`
    - `aborted`
    - `uploaded`
    - `failed`

    @property state
    @type {String}
    @default 'queued'
    @readonly
   */
  state: 'queued',

  upload(url, opts) {
    if (['queued', 'failed', 'timed_out'].indexOf(get(this, 'state')) === -1) {
      Ember.assert(`The file ${this.id} is in the state "${get(this, 'state')}" and cannot be requeued.`);
    }

    let options = normalizeOptions(this, url, opts);

    // Build the form
    let form = new FormData();

    Object.keys(options.data).forEach((key) => {
      if (key === options.fileKey) {
        form.append(key, options.data[key], get(this, 'name'));
      } else {
        form.append(key, options.data[key]);
      }
    });

    let request = new HTTPRequest();
    request.open(options.method, options.url);

    Object.keys(options.headers).forEach(function (key) {
      request.setRequestHeader(key, options.headers[key]);
    });

    if (options.timeout) {
      request.timeout = options.timeout;
    }

    request.onprogress = (evt) => {
      if (evt.lengthComputable) {
        set(this, 'loaded', evt.loaded);
        set(this, 'size', evt.total);
        set(this, 'progress', (evt.loaded / evt.total) * 100);
      }
    };

    request.ontimeout = () => {
      set(this, 'state', 'timed_out');
    };

    request.onabort = () => {
      set(this, 'state', 'aborted');
    };

    set(this, 'state', 'uploading');

    // Increment for Ember.Test
    inflightRequests++;

    return request.send(form).then((result) => {
      set(this, 'state', 'uploaded');
      return result;
    }, (error) => {
      set(this, 'state', 'failed');
      return RSVP.reject(error);
    }).finally(function () {
      // Decrement for Ember.Test
      inflightRequests--;
    });
  },

  read(options={ as: 'data-url' }) {
    let reader = new FileReader();

    let blob = this.blob;
    switch (options.as) {
    case 'array-buffer':
      return reader.readAsArrayBuffer(blob);
    case 'data-url':
      return reader.readAsDataURL(blob);
    case 'binary-string':
      return reader.readAsBinaryString(blob);
    case 'text':
      return reader.readAsText(blob);
    }
  }
}).reopenClass({

  /**
    Creates a file object that can be read or uploaded to a
    server from a Blob object.

    @method fromBlob
    @param {Blob} blob The blob to create the file from.
    @return {File} A file object
   */
  fromBlob(blob) {
    let file = this.create();
    Object.defineProperty(file, 'blob', {
      writeable: false,
      enumerable: false,
      value: blob
    });

    return file;
  },

  fromDataURL(dataURL) {
    let [typeInfo, base64String] = dataURL.split(',');
    let mimeType = typeInfo.match(/:(.*?);/)[1];

    let binaryString = atob(base64String);
    let binaryData = new Uint8Array(binaryString.length);

    for (let i = 0, len = binaryString.length; i < len; i++) {
      binaryData[i] = binaryString.charCodeAt(i);
    }

    let blob = new Blob([binaryData], { type: mimeType });

    return this.fromBlob(blob);
  }
});
<|endoftext|>


<commit_before>import { N_ } from '../i18n';
import {templateUrl} from '../shared/template-url/template-url.factory';
import editScheduleResolve from './editSchedule.resolve';

const jobTemplatesSchedulesListRoute = {
    searchPrefix: 'schedule',
    name: 'templates.editJobTemplate.schedules',
    route: '/schedules',
    data: {
        activityStream: true,
        activityStreamTarget: 'job_template',
    },
    ncyBreadcrumb: {
        label: N_('SCHEDULES')
    },
    resolve: {
        Dataset: ['ScheduleList', 'QuerySet', '$stateParams', 'GetBasePath',
            function(list, qs, $stateParams, GetBasePath) {
                let path = `${GetBasePath('job_templates')}${$stateParams.job_template_id}/schedules`;
                return qs.search(path, $stateParams[`${list.iterator}_search`]);
            }
        ],
        ParentObject: ['$stateParams', 'Rest', 'GetBasePath', function($stateParams, Rest, GetBasePath){
            let path = `${GetBasePath('job_templates')}${$stateParams.job_template_id}`;
            Rest.setUrl(path);
            return Rest.get(path).then(response => response.data);
        }],
        UnifiedJobsOptions: ['Rest', 'GetBasePath', '$q',
            function(Rest, GetBasePath, $q) {
                Rest.setUrl(GetBasePath('unified_jobs'));
                var val = $q.defer();
                Rest.options()
                    .then(function(data) {
                        val.resolve(data.data);
                    }, function(data) {
                        val.reject(data);
                    });
                return val.promise;
            }],
        ScheduleList: ['SchedulesList', 'GetBasePath', '$stateParams',
            (SchedulesList, GetBasePath, $stateParams) => {
                let list = _.cloneDeep(SchedulesList);
                list.basePath = GetBasePath('job_templates') + $stateParams.job_template_id + '/schedules/';
                return list;
            }
        ]
    },
    views: {
        related: {
            templateProvider: function(ScheduleList, generateList){
                ScheduleList.title = false;
                let html = generateList.build({
                    list: ScheduleList,
                    mode: 'edit'
                });
                return html;
            },
            controller: 'schedulerListController'
        }
    }
};

const jobTemplatesSchedulesAddRoute = {
    name: 'templates.editJobTemplate.schedules.add',
    route: '/add',
    views: {
        'scheduler@templates': {
            controller: 'schedulerAddController',
            templateUrl: templateUrl("scheduler/schedulerForm"),
        }
    },
    ncyBreadcrumb: {
        label: N_('CREATE SCHEDULE')
    }
};

const jobTemplatesSchedulesEditRoute = {
    name: 'templates.editJobTemplate.schedules.edit',
    route: '/:schedule_id',
    views: {
        'scheduler@templates': {
            controller: 'schedulerEditController',
            templateUrl: templateUrl("scheduler/schedulerForm"),
        }
    },
    ncyBreadcrumb: {
        label: "{{breadcrumb.schedule_name}}"
    },
    resolve: editScheduleResolve()
};

// workflows
const workflowSchedulesRoute = {
    searchPrefix: 'schedule',
    name: 'templates.editWorkflowJobTemplate.schedules',
    route: '/schedules',
    data: {
        activityStream: true,
        activityStreamTarget: 'workflow_job_template',
    },
    ncyBreadcrumb: {
        label: N_('SCHEDULES')
    },
    resolve: {
        Dataset: ['ScheduleList', 'QuerySet', '$stateParams', 'GetBasePath',
            function(list, qs, $stateParams, GetBasePath) {
                let path = `${GetBasePath('workflow_job_templates')}${$stateParams.workflow_job_template_id}/schedules`;
                return qs.search(path, $stateParams[`${list.iterator}_search`]);
            }
        ],
        ParentObject: ['$stateParams', 'Rest', 'GetBasePath', function($stateParams, Rest, GetBasePath){
            let path = `${GetBasePath('workflow_job_templates')}${$stateParams.workflow_job_template_id}`;
            Rest.setUrl(path);
            return Rest.get(path).then(response => response.data);
        }],
        UnifiedJobsOptions: ['Rest', 'GetBasePath', '$stateParams', '$q',
            function(Rest, GetBasePath, $stateParams, $q) {
                Rest.setUrl(GetBasePath('unified_jobs'));
                var val = $q.defer();
                Rest.options()
                    .then(function(data) {
                        val.resolve(data.data);
                    }, function(data) {
                        val.reject(data);
                    });
                return val.promise;
            }],
        ScheduleList: ['SchedulesList', 'GetBasePath', '$stateParams',
            (SchedulesList, GetBasePath, $stateParams) => {
                let list = _.cloneDeep(SchedulesList);
                list.basePath = GetBasePath('workflow_job_templates') + $stateParams.workflow_job_template_id + '/schedules/';
                return list;
            }
        ]
    },
    views: {
        related: {
            templateProvider: function(ScheduleList, generateList){
                ScheduleList.title = false;
                let html = generateList.build({
                    list: ScheduleList,
                    mode: 'edit'
                });
                return html;
            },
            controller: 'schedulerListController'
        }
    }
};

const workflowSchedulesAddRoute = {
    name: 'templates.editWorkflowJobTemplate.schedules.add',
    route: '/add',
    views: {
        'scheduler@templates': {
            controller: 'schedulerAddController',
            templateUrl: templateUrl("scheduler/schedulerForm"),
        }
    },
    ncyBreadcrumb: {
        label: N_('CREATE SCHEDULE')
    }
};

const workflowSchedulesEditRoute = {
    name: 'templates.editWorkflowJobTemplate.schedules.edit',
    route: '/:schedule_id',
    views: {
        'scheduler@templates': {
            controller: 'schedulerEditController',
            templateUrl: templateUrl("scheduler/schedulerForm"),
        }
    },
    ncyBreadcrumb: {
        label: '{{breadcrumb.schedule_name}}'
    },
    resolve: editScheduleResolve()
};

const projectsSchedulesListRoute = {
    searchPrefix: 'schedule',
    name: 'projects.edit.schedules',
    route: '/schedules',
    data: {
        activityStream: true,
        activityStreamTarget: 'project',
    },
    ncyBreadcrumb: {
        label: N_('SCHEDULES')
    },
    resolve: {
        Dataset: ['ScheduleList', 'QuerySet', '$stateParams', 'GetBasePath',
            function(list, qs, $stateParams, GetBasePath) {
                let path = `${GetBasePath('projects')}${$stateParams.project_id}/schedules`;
                return qs.search(path, $stateParams[`${list.iterator}_search`]);
            }
        ],
        ParentObject: ['$stateParams', 'Rest', 'GetBasePath', function($stateParams, Rest, GetBasePath){
            let path = `${GetBasePath('projects')}${$stateParams.project_id}`;
            Rest.setUrl(path);
            return Rest.get(path).then(response => response.data);
        }],
        UnifiedJobsOptions: ['Rest', 'GetBasePath', '$stateParams', '$q',
            function(Rest, GetBasePath, $stateParams, $q) {
                Rest.setUrl(GetBasePath('unified_jobs'));
                var val = $q.defer();
                Rest.options()
                    .then(function(data) {
                        val.resolve(data.data);
                    }, function(data) {
                        val.reject(data);
                    });
                return val.promise;
            }],
        ScheduleList: ['SchedulesList', 'GetBasePath', '$stateParams',
            (SchedulesList, GetBasePath, $stateParams) => {
                let list = _.cloneDeep(SchedulesList);
                list.basePath = GetBasePath('projects') + $stateParams.project_id + '/schedules/';
                return list;
            }
        ]
    },
    views: {
        related: {
            templateProvider: function(ScheduleList, generateList){
                ScheduleList.title = false;
                let html = generateList.build({
                    list: ScheduleList,
                    mode: 'edit'
                });
                return html;
            },
            controller: 'schedulerListController'
        }
    }
};

const projectsSchedulesAddRoute = {
    name: 'projects.edit.schedules.add',
    route: '/add',
    ncyBreadcrumb: {
        label: N_('CREATE SCHEDULE')
    },
    views: {
        'scheduler@projects': {
            controller: 'schedulerAddController',
            templateUrl: templateUrl("scheduler/schedulerForm"),
        }
    }
};

const projectsSchedulesEditRoute = {
    name: 'projects.edit.schedules.edit',
    route: '/:schedule_id',
    ncyBreadcrumb: {
        label: "{{breadcrumb.schedule_name}}"
    },
    views: {
        'scheduler@projects': {
            controller: 'schedulerEditController',
            templateUrl: templateUrl("scheduler/schedulerForm"),
        }
    },
    resolve: editScheduleResolve()
};

const jobsSchedulesRoute = {
    searchPrefix: 'schedule',
    name: 'schedules',
    route: '/schedules',
    params: {
        schedule_search: {
            value: {
                next_run__isnull: 'false',
                order_by: 'unified_job_template__polymorphic_ctype__model'
            },
            dynamic: true
        }
    },
    data: {
        activityStream: false,
    },
    ncyBreadcrumb: {
        label: N_('SCHEDULES')
    },
    resolve: {
        ScheduleList: ['ScheduledJobsList', function(list){
            return list;
        }],
        Dataset: ['ScheduleList', 'QuerySet', '$stateParams', 'GetBasePath',
            function(list, qs, $stateParams, GetBasePath) {
                let path = GetBasePath('schedules');
                return qs.search(path, $stateParams[`${list.iterator}_search`]);
            }
        ],
        ParentObject: ['GetBasePath', (GetBasePath) =>{return {endpoint:GetBasePath('schedules')}; }],
        UnifiedJobsOptions: ['Rest', 'GetBasePath', '$stateParams', '$q',
            function(Rest, GetBasePath, $stateParams, $q) {
                Rest.setUrl(GetBasePath('unified_jobs'));
                var val = $q.defer();
                Rest.options()
                    .then(function(data) {
                        val.resolve(data.data);
                    }, function(data) {
                        val.reject(data);
                    });
                return val.promise;
            }]
    },
    views: {
        '@': {
            templateProvider: function(ScheduleList, generateList){

                let html = generateList.build({
                    list: ScheduleList,
                    mode: 'edit'
                });
                html = generateList.wrapPanel(html);
                let formPlaceholder = generateList.insertFormView();
                html = formPlaceholder + html;
                return html;
            },
            controller: 'schedulerListController'
        }
    }
};

// the /#/jobs/schedules/:schedule_id state needs to know about the type of
// resource is being scheduled.
const parentResolve = {
    ParentObject: ['$stateParams', 'Rest', 'GetBasePath', 'scheduleResolve',
        function($stateParams, Rest, GetBasePath, scheduleResolve){
            let path = scheduleResolve.related.unified_job_template;
            Rest.setUrl(path);
            return Rest.get(path).then(response => response.data);
        }
    ]
};

const jobsSchedulesEditRoute = {
    name: 'schedules.edit',
    route: '/:schedule_id',
    ncyBreadcrumb: {
        parent: 'schedules',
        label: "{{breadcrumb.schedule_name}}"
    },
    views: {
        'form':{
            templateProvider: function(ParentObject, Rest){
                let path;
                if(ParentObject.type === 'system_job_template'){
                    path = templateUrl('management-jobs/scheduler/schedulerForm');
                }
                else {
                    path = templateUrl('scheduler/schedulerForm');
                }
                Rest.setUrl(path);
                return Rest.get(path).then(response => response.data);
            },
            controllerProvider: function(ParentObject){
                if (ParentObject.type === 'system_job_template') {
                    return 'managementJobEditController';
                }
                else {
                    return 'schedulerEditController';
                }
            }
        }
    },
    resolve: _.merge(editScheduleResolve(), parentResolve)
};

export {
    jobTemplatesSchedulesListRoute,
    jobTemplatesSchedulesAddRoute,
    jobTemplatesSchedulesEditRoute,
    workflowSchedulesRoute,
    workflowSchedulesAddRoute,
    workflowSchedulesEditRoute,
    projectsSchedulesListRoute,
    projectsSchedulesAddRoute,
    projectsSchedulesEditRoute,
    jobsSchedulesRoute,
    jobsSchedulesEditRoute
};
<commit_msg>Switching to $http service instead of Rest service.<commit_after>import { N_ } from '../i18n';
import {templateUrl} from '../shared/template-url/template-url.factory';
import editScheduleResolve from './editSchedule.resolve';

const jobTemplatesSchedulesListRoute = {
    searchPrefix: 'schedule',
    name: 'templates.editJobTemplate.schedules',
    route: '/schedules',
    data: {
        activityStream: true,
        activityStreamTarget: 'job_template',
    },
    ncyBreadcrumb: {
        label: N_('SCHEDULES')
    },
    resolve: {
        Dataset: ['ScheduleList', 'QuerySet', '$stateParams', 'GetBasePath',
            function(list, qs, $stateParams, GetBasePath) {
                let path = `${GetBasePath('job_templates')}${$stateParams.job_template_id}/schedules`;
                return qs.search(path, $stateParams[`${list.iterator}_search`]);
            }
        ],
        ParentObject: ['$stateParams', 'Rest', 'GetBasePath', function($stateParams, Rest, GetBasePath){
            let path = `${GetBasePath('job_templates')}${$stateParams.job_template_id}`;
            Rest.setUrl(path);
            return Rest.get(path).then(response => response.data);
        }],
        UnifiedJobsOptions: ['Rest', 'GetBasePath', '$q',
            function(Rest, GetBasePath, $q) {
                Rest.setUrl(GetBasePath('unified_jobs'));
                var val = $q.defer();
                Rest.options()
                    .then(function(data) {
                        val.resolve(data.data);
                    }, function(data) {
                        val.reject(data);
                    });
                return val.promise;
            }],
        ScheduleList: ['SchedulesList', 'GetBasePath', '$stateParams',
            (SchedulesList, GetBasePath, $stateParams) => {
                let list = _.cloneDeep(SchedulesList);
                list.basePath = GetBasePath('job_templates') + $stateParams.job_template_id + '/schedules/';
                return list;
            }
        ]
    },
    views: {
        related: {
            templateProvider: function(ScheduleList, generateList){
                ScheduleList.title = false;
                let html = generateList.build({
                    list: ScheduleList,
                    mode: 'edit'
                });
                return html;
            },
            controller: 'schedulerListController'
        }
    }
};

const jobTemplatesSchedulesAddRoute = {
    name: 'templates.editJobTemplate.schedules.add',
    route: '/add',
    views: {
        'scheduler@templates': {
            controller: 'schedulerAddController',
            templateUrl: templateUrl("scheduler/schedulerForm"),
        }
    },
    ncyBreadcrumb: {
        label: N_('CREATE SCHEDULE')
    }
};

const jobTemplatesSchedulesEditRoute = {
    name: 'templates.editJobTemplate.schedules.edit',
    route: '/:schedule_id',
    views: {
        'scheduler@templates': {
            controller: 'schedulerEditController',
            templateUrl: templateUrl("scheduler/schedulerForm"),
        }
    },
    ncyBreadcrumb: {
        label: "{{breadcrumb.schedule_name}}"
    },
    resolve: editScheduleResolve()
};

// workflows
const workflowSchedulesRoute = {
    searchPrefix: 'schedule',
    name: 'templates.editWorkflowJobTemplate.schedules',
    route: '/schedules',
    data: {
        activityStream: true,
        activityStreamTarget: 'workflow_job_template',
    },
    ncyBreadcrumb: {
        label: N_('SCHEDULES')
    },
    resolve: {
        Dataset: ['ScheduleList', 'QuerySet', '$stateParams', 'GetBasePath',
            function(list, qs, $stateParams, GetBasePath) {
                let path = `${GetBasePath('workflow_job_templates')}${$stateParams.workflow_job_template_id}/schedules`;
                return qs.search(path, $stateParams[`${list.iterator}_search`]);
            }
        ],
        ParentObject: ['$stateParams', 'Rest', 'GetBasePath', function($stateParams, Rest, GetBasePath){
            let path = `${GetBasePath('workflow_job_templates')}${$stateParams.workflow_job_template_id}`;
            Rest.setUrl(path);
            return Rest.get(path).then(response => response.data);
        }],
        UnifiedJobsOptions: ['Rest', 'GetBasePath', '$stateParams', '$q',
            function(Rest, GetBasePath, $stateParams, $q) {
                Rest.setUrl(GetBasePath('unified_jobs'));
                var val = $q.defer();
                Rest.options()
                    .then(function(data) {
                        val.resolve(data.data);
                    }, function(data) {
                        val.reject(data);
                    });
                return val.promise;
            }],
        ScheduleList: ['SchedulesList', 'GetBasePath', '$stateParams',
            (SchedulesList, GetBasePath, $stateParams) => {
                let list = _.cloneDeep(SchedulesList);
                list.basePath = GetBasePath('workflow_job_templates') + $stateParams.workflow_job_template_id + '/schedules/';
                return list;
            }
        ]
    },
    views: {
        related: {
            templateProvider: function(ScheduleList, generateList){
                ScheduleList.title = false;
                let html = generateList.build({
                    list: ScheduleList,
                    mode: 'edit'
                });
                return html;
            },
            controller: 'schedulerListController'
        }
    }
};

const workflowSchedulesAddRoute = {
    name: 'templates.editWorkflowJobTemplate.schedules.add',
    route: '/add',
    views: {
        'scheduler@templates': {
            controller: 'schedulerAddController',
            templateUrl: templateUrl("scheduler/schedulerForm"),
        }
    },
    ncyBreadcrumb: {
        label: N_('CREATE SCHEDULE')
    }
};

const workflowSchedulesEditRoute = {
    name: 'templates.editWorkflowJobTemplate.schedules.edit',
    route: '/:schedule_id',
    views: {
        'scheduler@templates': {
            controller: 'schedulerEditController',
            templateUrl: templateUrl("scheduler/schedulerForm"),
        }
    },
    ncyBreadcrumb: {
        label: '{{breadcrumb.schedule_name}}'
    },
    resolve: editScheduleResolve()
};

const projectsSchedulesListRoute = {
    searchPrefix: 'schedule',
    name: 'projects.edit.schedules',
    route: '/schedules',
    data: {
        activityStream: true,
        activityStreamTarget: 'project',
    },
    ncyBreadcrumb: {
        label: N_('SCHEDULES')
    },
    resolve: {
        Dataset: ['ScheduleList', 'QuerySet', '$stateParams', 'GetBasePath',
            function(list, qs, $stateParams, GetBasePath) {
                let path = `${GetBasePath('projects')}${$stateParams.project_id}/schedules`;
                return qs.search(path, $stateParams[`${list.iterator}_search`]);
            }
        ],
        ParentObject: ['$stateParams', 'Rest', 'GetBasePath', function($stateParams, Rest, GetBasePath){
            let path = `${GetBasePath('projects')}${$stateParams.project_id}`;
            Rest.setUrl(path);
            return Rest.get(path).then(response => response.data);
        }],
        UnifiedJobsOptions: ['Rest', 'GetBasePath', '$stateParams', '$q',
            function(Rest, GetBasePath, $stateParams, $q) {
                Rest.setUrl(GetBasePath('unified_jobs'));
                var val = $q.defer();
                Rest.options()
                    .then(function(data) {
                        val.resolve(data.data);
                    }, function(data) {
                        val.reject(data);
                    });
                return val.promise;
            }],
        ScheduleList: ['SchedulesList', 'GetBasePath', '$stateParams',
            (SchedulesList, GetBasePath, $stateParams) => {
                let list = _.cloneDeep(SchedulesList);
                list.basePath = GetBasePath('projects') + $stateParams.project_id + '/schedules/';
                return list;
            }
        ]
    },
    views: {
        related: {
            templateProvider: function(ScheduleList, generateList){
                ScheduleList.title = false;
                let html = generateList.build({
                    list: ScheduleList,
                    mode: 'edit'
                });
                return html;
            },
            controller: 'schedulerListController'
        }
    }
};

const projectsSchedulesAddRoute = {
    name: 'projects.edit.schedules.add',
    route: '/add',
    ncyBreadcrumb: {
        label: N_('CREATE SCHEDULE')
    },
    views: {
        'scheduler@projects': {
            controller: 'schedulerAddController',
            templateUrl: templateUrl("scheduler/schedulerForm"),
        }
    }
};

const projectsSchedulesEditRoute = {
    name: 'projects.edit.schedules.edit',
    route: '/:schedule_id',
    ncyBreadcrumb: {
        label: "{{breadcrumb.schedule_name}}"
    },
    views: {
        'scheduler@projects': {
            controller: 'schedulerEditController',
            templateUrl: templateUrl("scheduler/schedulerForm"),
        }
    },
    resolve: editScheduleResolve()
};

const jobsSchedulesRoute = {
    searchPrefix: 'schedule',
    name: 'schedules',
    route: '/schedules',
    params: {
        schedule_search: {
            value: {
                next_run__isnull: 'false',
                order_by: 'unified_job_template__polymorphic_ctype__model'
            },
            dynamic: true
        }
    },
    data: {
        activityStream: false,
    },
    ncyBreadcrumb: {
        label: N_('SCHEDULES')
    },
    resolve: {
        ScheduleList: ['ScheduledJobsList', function(list){
            return list;
        }],
        Dataset: ['ScheduleList', 'QuerySet', '$stateParams', 'GetBasePath',
            function(list, qs, $stateParams, GetBasePath) {
                let path = GetBasePath('schedules');
                return qs.search(path, $stateParams[`${list.iterator}_search`]);
            }
        ],
        ParentObject: ['GetBasePath', (GetBasePath) =>{return {endpoint:GetBasePath('schedules')}; }],
        UnifiedJobsOptions: ['Rest', 'GetBasePath', '$stateParams', '$q',
            function(Rest, GetBasePath, $stateParams, $q) {
                Rest.setUrl(GetBasePath('unified_jobs'));
                var val = $q.defer();
                Rest.options()
                    .then(function(data) {
                        val.resolve(data.data);
                    }, function(data) {
                        val.reject(data);
                    });
                return val.promise;
            }]
    },
    views: {
        '@': {
            templateProvider: function(ScheduleList, generateList){

                let html = generateList.build({
                    list: ScheduleList,
                    mode: 'edit'
                });
                html = generateList.wrapPanel(html);
                let formPlaceholder = generateList.insertFormView();
                html = formPlaceholder + html;
                return html;
            },
            controller: 'schedulerListController'
        }
    }
};

// the /#/jobs/schedules/:schedule_id state needs to know about the type of
// resource is being scheduled.
const parentResolve = {
    ParentObject: ['$stateParams', 'Rest', 'GetBasePath', 'scheduleResolve',
        function($stateParams, Rest, GetBasePath, scheduleResolve){
            let path = scheduleResolve.related.unified_job_template;
            Rest.setUrl(path);
            return Rest.get(path).then(response => response.data);
        }
    ]
};

const jobsSchedulesEditRoute = {
    name: 'schedules.edit',
    route: '/:schedule_id',
    ncyBreadcrumb: {
        parent: 'schedules',
        label: "{{breadcrumb.schedule_name}}"
    },
    views: {
        'form':{
            templateProvider: function(ParentObject, $http){
                let path;
                if(ParentObject.type === 'system_job_template'){
                    path = templateUrl('management-jobs/scheduler/schedulerForm');
                }
                else {
                    path = templateUrl('scheduler/schedulerForm');
                }
                return $http.get(path).then(response => response.data);
            },
            controllerProvider: function(ParentObject){
                if (ParentObject.type === 'system_job_template') {
                    return 'managementJobEditController';
                }
                else {
                    return 'schedulerEditController';
                }
            }
        }
    },
    resolve: _.merge(editScheduleResolve(), parentResolve)
};

export {
    jobTemplatesSchedulesListRoute,
    jobTemplatesSchedulesAddRoute,
    jobTemplatesSchedulesEditRoute,
    workflowSchedulesRoute,
    workflowSchedulesAddRoute,
    workflowSchedulesEditRoute,
    projectsSchedulesListRoute,
    projectsSchedulesAddRoute,
    projectsSchedulesEditRoute,
    jobsSchedulesRoute,
    jobsSchedulesEditRoute
};
<|endoftext|>


<commit_before>/* Toggles selected option class
* .block-label > label > input
*/
module.exports = (function() {
  $('.options').each(function(i, container){
    var blocklabels = $(container).find('.block-label'),
      labels = blocklabels.find('label');

    labels.each(function(i, el){
      var label = $(el),
        input = label.find('input');

      if(input.is(':checked')){
        label.addClass('selected');
      }

      input.on('change', function(){
        var checked = input.is(':checked');
        if(input.is(':radio')){
            labels.removeClass('selected');
        }
        label.toggleClass('selected', checked);
      });
    });
  });
})();
<commit_msg>rewrote JS to be more cross-browser/robust<commit_after>/* Toggles selected option class, assumes the following structure
* .options > .block-label + .block-label > label > input[:radio || :checkbox]
*/

module.exports = (function() {

  var selectedOption = {},
  options = $('.options'),
  change_class = 'selected',
  focusblur_class = 'add-focus';

  selectedOption.init = function() {
    options.each(function(i, container){
      var input_group = $(container).find('.block-label');

      selectedOption.bindInputEvents(input_group);

    });
  };

  selectedOption.bindInputEvents = function(input_group) {
    input_group.each(function(i, blocklabel){
      var container = $(blocklabel),
      siblings = container.siblings().find('label'),
      input = container.find('input'),
      is_radio = input.is(':radio'),
      label = container.find('label');

      input.on('change', function(){
        var checked = input.is(':checked');

        if(checked && is_radio) {
          siblings.removeClass(change_class);
        }

        label.toggleClass(change_class, checked);

      })
      .on('focus', function(){
        label.addClass(focusblur_class);
      })
      .on('blur', function(){
        label.removeClass(focusblur_class);
      })
      .change();
    });
  };

  selectedOption.init();

  return selectedOption;

})();
<|endoftext|>


<commit_before>// adapted from the rasterize.js example coming with PhantomJS
//
// Edit the fillLoginInfo function below (i.e., add credentials for logging into
// the web site) and put this file into the same directory as the phantomjs
// binary (as referenced in the settings file)

var page = require('webpage').create(),
    system = require('system'),
    address, output, size;

address = system.args[1];
output = system.args[2];
// page.viewportSize = { width: 1500, height: 1500 };
if (system.args.length > 3 && system.args[2].substr(-4) === ".pdf") {
    size = system.args[3].split('*');
    page.paperSize = size.length === 2 ? { width: size[0], height: size[1], margin: '0px' }
                                        : { format: system.args[3], orientation: 'portrait', margin: '1cm' };
}

var fillLoginInfo = function(){
    var frm = document.getElementById("login_form");
    $("#id_identification").val("username");
    $("#id_password").val("password");
    frm.submit();
}

page.onLoadFinished = function(){
    if(page.title == "Unsere Familiengeschichte - Anmeldung"){
        page.evaluate(fillLoginInfo);
        return;
    }
    else {
        // currently clipRect is ignored for pdf output (so we have to apply
        // pdfcrop afterwards ...)
        page.clipRect = page.evaluate(function() {
            return document.getElementById("familytree_svg").getBoundingClientRect();
        });
        page.render(output);
        phantom.exit();
    }
}

page.open(address);
<commit_msg>Add timeout to PhantomJS rasterize script.<commit_after>// adapted from the rasterize.js example coming with PhantomJS
//
// Edit the fillLoginInfo function below (i.e., add credentials for logging into
// the web site) and put this file into the same directory as the phantomjs
// binary (as referenced in the settings file)

var page = require('webpage').create(),
    system = require('system'),
    address, output, size;

page.settings.resourceTimeout = 10000;

address = system.args[1];
output = system.args[2];
// page.viewportSize = { width: 1500, height: 1500 };
if (system.args.length > 3 && system.args[2].substr(-4) === ".pdf") {
    size = system.args[3].split('*');
    page.paperSize = size.length === 2 ? { width: size[0], height: size[1], margin: '0px' }
                                        : { format: system.args[3], orientation: 'portrait', margin: '1cm' };
}

var fillLoginInfo = function(){
    var frm = document.getElementById("login_form");
    $("#id_identification").val("username");
    $("#id_password").val("password");
    frm.submit();
}

page.onLoadFinished = function(){
    if(page.title == "Unsere Familiengeschichte - Anmeldung"){
        page.evaluate(fillLoginInfo);
        return;
    }
    else {
        // currently clipRect is ignored for pdf output (so we have to apply
        // pdfcrop afterwards ...)
        page.clipRect = page.evaluate(function() {
            return document.getElementById("familytree_svg").getBoundingClientRect();
        });
        page.render(output);
        phantom.exit();
    }
}

page.open(address);
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import {
  AppRegistry,
  StyleSheet,
  Text,
  View,
  Alert,
  ListView,
  LayoutAnimation,
  Linking
} from 'react-native';
import {Button} from 'react-native-elements';
import LocationList from './components/LocationList';
import RenameModal from './components/RenameModal';
import MapModal from './components/MapModal';
import update from 'immutability-helper';

export default class reactNativeProject extends Component {

  constructor(props) {
    super(props);
    this.state = {
      lastPosition: {
        coords: {
          latitude: 37.972025,
          longitude: 23.725979
        }
      },
      locationsArray: {
        locations: [
        ]
      },
      //rename modal
      modalIsVisible: false,
      //Map modal visibility
      isMapModalVisible: false,
    };
  }

  state = {
    buttonDisable: true,
    buttonTitle: 'Loading your location....',
    initialPosition: 'uknown',
    lastPosition: 'unknown',
    //rename modal
    modalIsVisible: false,
    //is used to find the item on the array of locations and change its name on save
    locationIndexInModal: '0',
    //Map modal visibility
    isMapModalVisible: false,
    // the location that rename modal shows
    modalLocationName: 'Dummy Location',
    locationsArray: {
      locations: [
      ]
    }
  };

  watchID: ?number = null;

  componentDidMount() {
    this._findUserPosition();
  }

  componentWillUnmount() {
    navigator.geolocation.clearWatch(this.watchID);
  }

  _findUserPosition = () => {
    navigator.geolocation.getCurrentPosition(
      (position) => {
        var initialPosition = position;
        this.setState({initialPosition});
        console.log(position);
      },
      (error) => alert(JSON.stringify(error)),
      {enableHighAccuracy: true, timeout: 100000, maximumAge: 1000}
    );

    this.watchID = navigator.geolocation.watchPosition((position) => {
      // position.name = 'Location ' + this.state.locationsArray.locations.length + 1;
      // console.log(position.name);
      var lastPosition = position;
      // TODO: check why allowing this console is run many times - might be from
      // the geolocation trying to find the exact location of the user
      // console.log(position);
      this.setState({
        lastPosition: position
      });
      this.setState({
        buttonDisable: false,
        buttonTitle: 'Add my location'
      });
    });
  }

  // gets the location from geolocation and saves it to the location array
  _getLocation = () => {
    var lastPositionWithName = { ...this.state.lastPosition };
    console.log(this.state.lastPosition);
    lastPositionWithName["name"] = 'Location ' + parseInt(this.state.locationsArray.locations.length + 1);
    var locations = this.state.locationsArray.locations;
    locations.push(lastPositionWithName);
    this.setState({
      locations: locations
    });
  }

  //gets the location from marker and saves it to the location array
  _getMarkerLocation = (location) => {
    //location parameter is the region object in the state of mapmodal
    var newMarkerLocation = {};
    newMarkerLocation["name"] = 'Marker Location ' + parseInt(this.state.locationsArray.locations.length + 1);
    newMarkerLocation["coords"] = { ...location }
    var locations = this.state.locationsArray.locations;
    locations.push(newMarkerLocation);
    this.setState({
      locations: locations
    });
    // and finally close the modal
    this._closeMapModal();
  }

  _deleteRow = (rowID) => {
    LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
    this.setState({
      locations: this.state.locationsArray.locations.splice(rowID, 1)
    });
  }

  _renameRow = (rowData, rowID) => {
    // first show the modal
    this._setModalVisible(true, rowID);
    // change the name inside the modal
    this.setState({
      modalLocationName: rowData.name
    })
  }

  _onClickRow = (rowData) => {
    locationObject = rowData;
    var url = 'http://maps.google.com/maps?q=loc:' + locationObject.coords.latitude + '+' + locationObject.coords.longitude;
    // var url = 'http://www.google.com';

    Linking.canOpenURL(url).then(supported => {
      if (!supported) {
        console.log('Can\'t handle url: ' + url);
      } else {
        return Linking.openURL(url);
      }
    }).catch(err => console.error('An error occurred', err));
  }

  _setModalVisible = (visible, rowID) => {
    // as we open the rename modal we need to set visibility of modal to true
    // but also the index of that location inside the textinput so we know
    // which location we will edit later when user clicks save
    this.setState({
      modalIsVisible: visible,
      locationIndexInModal: rowID
    });
  }

  _modalLocationName = (name) => {
    // sets the location that will be inside the textinput
    // inside the modal
    this.setState({
      modalLocationName: name
    })
  }
  // this function is triggered when you click save inside the rename modal
  _renameLocation = (name) => {
    //hide the modal since you clicked save
    var indexed = this.state.locationIndexInModal;
    this._setModalVisible(false);
    // add the old array in a var
    var oldLocationArray = this.state.locationsArray.locations;
    oldLocationArray[indexed].name = name
    this.setState({
      oldLocationArray
    });
  }

  _showMapModal = () => {
    this.setState({
      'isMapModalVisible': true
    })
  }

  _closeMapModal = () => {
    this.setState({
      'isMapModalVisible': false
    })
  }

  render() {
    return (
      <View style={styles.container}>
        <Text style={styles.welcome}>
          Welcome to locationSaver!
        </Text>
        <Text style={styles.instructions}>
          Click the button to save your location to the list
        </Text>
        <View>
          <Button
            raised icon={{name: 'room'}}
            title={this.state.buttonTitle}
            buttonStyle={styles.button}
            onPress={this._getLocation}
            disabled={this.state.buttonDisable}
            accessibilityLabel="Add your location"
          />
          <Button
            title='Open Modal'
            buttonStyle={styles.button}
            onPress={this._showMapModal}
          />
        </View>
        <View style={styles.locationListContainer}>
          <LocationList
            key={this._data}
            style={styles.locationList}
            dataSource={this.state.locationsArray.locations}
            onDelete={ this._deleteRow }
            onRename={ this._renameRow }
            onClickRow={ this._onClickRow }
          />
        </View>
        <View>
          <RenameModal
            isVisible={ this.state.modalIsVisible }
            setModalVisible={ this._setModalVisible }
            renameLocation={ this._renameLocation }
            locationName={ this.state.modalLocationName }
            onChangeCallback={ this._modalLocationName }
            />
        </View>
        <MapModal
          isMapModalVisible={ this.state.isMapModalVisible }
          closeMapModal={ this._closeMapModal }
          initialLatitude={ this.state.lastPosition.coords.latitude }
          initialLongitude={ this.state.lastPosition.coords.longitude }
          onFindLocation={ this._findUserPosition }
          onAddLocation={ this._getMarkerLocation }
        />
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    // justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
    paddingTop: 20,
  },
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
  instructions: {
    textAlign: 'center',
    color: '#333333',
    marginBottom: 5,
  },
  locationListContainer: {
    flex: 1,
    alignSelf: 'stretch'
  },
  locationList: {
    alignSelf: 'stretch'
  },
  button: {
    backgroundColor: '#00998a',
    borderRadius: 5,
    marginTop: 20
  }
});

AppRegistry.registerComponent('reactNativeProject', () => reactNativeProject);
<commit_msg>updated app to save data to async storage<commit_after>import React, { Component } from 'react';
import {
  AppRegistry,
  StyleSheet,
  Text,
  View,
  Alert,
  ListView,
  LayoutAnimation,
  Linking,
  AsyncStorage
} from 'react-native';
import {Button} from 'react-native-elements';
import LocationList from './components/LocationList';
import RenameModal from './components/RenameModal';
import MapModal from './components/MapModal';
import update from 'immutability-helper';

var STORAGE_KEY = '@MySuperStore:key';

export default class reactNativeProject extends Component {

  constructor(props) {
    super(props);
    this.state = {
      lastPosition: {
        coords: {
          latitude: 37.972025,
          longitude: 23.725979
        }
      },
      locationsArray: {
        locations: [
        ]
      },
      //rename modal
      modalIsVisible: false,
      //Map modal visibility
      isMapModalVisible: false,
      messages: []
    };
  }

  state = {
    buttonDisable: true,
    buttonTitle: 'Loading your location....',
    initialPosition: 'uknown',
    lastPosition: 'unknown',
    //rename modal
    modalIsVisible: false,
    //is used to find the item on the array of locations and change its name on save
    locationIndexInModal: '0',
    //Map modal visibility
    isMapModalVisible: false,
    // the location that rename modal shows
    modalLocationName: 'Dummy Location',
    locationsArray: {
      locations: [
      ]
    }
  };

  watchID: ?number = null;

  componentDidMount() {
    this._loadInitialState().done();
    this._findUserPosition();
  }

  _loadInitialState = async () => {
    //try to find locations from localstorage
    try {
      var value = await AsyncStorage.getItem(STORAGE_KEY);
      if (value !== null){
        //setting the state will trigger the render inside the list
        this.setState({
          locationsArray: {
            locations: JSON.parse(value)
          }
        });
        this._appendMessage('Recovered selection from disk: ' + value);
      }
      else {
        this._appendMessage('Initialized with no selection on disk.');
      }
    } catch (error) {
      this._appendMessage('AsyncStorage error: ' + error.message);
    }
  }

  componentWillUnmount() {
    navigator.geolocation.clearWatch(this.watchID);
  }

  _findUserPosition = () => {
    navigator.geolocation.getCurrentPosition(
      (position) => {
        var initialPosition = position;
        this.setState({initialPosition});
        console.log(position);
      },
      (error) => alert(JSON.stringify(error)),
      {enableHighAccuracy: true, timeout: 100000, maximumAge: 1000}
    );

    this.watchID = navigator.geolocation.watchPosition((position) => {
      // position.name = 'Location ' + this.state.locationsArray.locations.length + 1;
      // console.log(position.name);
      var lastPosition = position;
      // TODO: check why allowing this console is run many times - might be from
      // the geolocation trying to find the exact location of the user
      // console.log(position);
      this.setState({
        lastPosition: position
      });
      this.setState({
        buttonDisable: false,
        buttonTitle: 'Add my location'
      });
    });
  }

  // gets the location from geolocation and saves it to the location array
  _getLocation = () => {
    var lastPositionWithName = { ...this.state.lastPosition };
    console.log(this.state.lastPosition);
    lastPositionWithName["name"] = 'Location ' + parseInt(this.state.locationsArray.locations.length + 1);
    var locations = this.state.locationsArray.locations;
    locations.push(lastPositionWithName);
    this.setState({
      locations: locations
    });
    //save the new locations to local storage as well
    this._locationsTouched();
  }

  _locationsTouched = () => {
    this._saveLocationStorage(JSON.stringify(this.state.locationsArray.locations));
  }

  _saveLocationStorage = async(locations) => {
    try {
      await AsyncStorage.setItem(STORAGE_KEY, locations)
      this._appendMessage('Saved selection to disk: ' + locations);
    } catch (error) {
      this._appendMessage('AsyncStorage error: ' + error.message);
    }
  }

  //gets the location from marker and saves it to the location array
  _getMarkerLocation = (location) => {
    //location parameter is the region object in the state of mapmodal
    var newMarkerLocation = {};
    newMarkerLocation["name"] = 'Marker Location ' + parseInt(this.state.locationsArray.locations.length + 1);
    newMarkerLocation["coords"] = { ...location }
    var locations = this.state.locationsArray.locations;
    locations.push(newMarkerLocation);
    this.setState({
      locations: locations
    });
    this._locationsTouched();
    // and finally close the modal
    this._closeMapModal();
  }

  _deleteRow = async(rowID) => {
    LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
    this.setState({
      locations: this.state.locationsArray.locations.splice(rowID, 1)
    });
    //save the new locations to local storage as well since we deleted a row
    this._locationsTouched();
  }

  _renameRow = (rowData, rowID) => {
    // first show the modal
    this._setModalVisible(true, rowID);
    // change the name inside the modal
    this.setState({
      modalLocationName: rowData.name
    })
  }

  _onClickRow = (rowData) => {
    locationObject = rowData;
    var url = 'http://maps.google.com/maps?q=loc:' + locationObject.coords.latitude + '+' + locationObject.coords.longitude;
    // var url = 'http://www.google.com';

    Linking.canOpenURL(url).then(supported => {
      if (!supported) {
        console.log('Can\'t handle url: ' + url);
      } else {
        return Linking.openURL(url);
      }
    }).catch(err => console.error('An error occurred', err));
  }

  _setModalVisible = (visible, rowID) => {
    // as we open the rename modal we need to set visibility of modal to true
    // but also the index of that location inside the textinput so we know
    // which location we will edit later when user clicks save
    this.setState({
      modalIsVisible: visible,
      locationIndexInModal: rowID
    });
  }

  _modalLocationName = (name) => {
    // sets the location that will be inside the textinput
    // inside the modal
    this.setState({
      modalLocationName: name
    })
  }
  // this function is triggered when you click save inside the rename modal
  _renameLocation = (name) => {
    //hide the modal since you clicked save
    var indexed = this.state.locationIndexInModal;
    this._setModalVisible(false);
    // add the old array in a var
    var oldLocationArray = this.state.locationsArray.locations;
    oldLocationArray[indexed].name = name
    this.setState({
      oldLocationArray
    });
    //save the new locations to local storage as well
    this._locationsTouched();
  }

  _showMapModal = () => {
    this.setState({
      'isMapModalVisible': true
    })
  }

  _closeMapModal = () => {
    this.setState({
      'isMapModalVisible': false
    })
  }

  _appendMessage = (message) => {
    console.log(message);
    this.setState({messages: this.state.messages.concat(message)});
  };

  render() {
    return (
      <View style={styles.container}>
        <Text style={styles.welcome}>
          Welcome to locationSaver!
        </Text>
        <Text style={styles.instructions}>
          Click the button to save your location to the list
        </Text>
        <View>
          <Button
            raised icon={{name: 'room'}}
            title={this.state.buttonTitle}
            buttonStyle={styles.button}
            onPress={this._getLocation}
            disabled={this.state.buttonDisable}
            accessibilityLabel="Add your location"
          />
          <Button
            title='Open Modal'
            buttonStyle={styles.button}
            onPress={this._showMapModal}
          />
        </View>
        <View style={styles.locationListContainer}>
          <LocationList
            key={this._data}
            style={styles.locationList}
            dataSource={this.state.locationsArray.locations}
            onDelete={ this._deleteRow }
            onRename={ this._renameRow }
            onClickRow={ this._onClickRow }
          />
        </View>
        <View>
          <RenameModal
            isVisible={ this.state.modalIsVisible }
            setModalVisible={ this._setModalVisible }
            renameLocation={ this._renameLocation }
            locationName={ this.state.modalLocationName }
            onChangeCallback={ this._modalLocationName }
            />
        </View>
        <MapModal
          isMapModalVisible={ this.state.isMapModalVisible }
          closeMapModal={ this._closeMapModal }
          initialLatitude={ this.state.lastPosition.coords.latitude }
          initialLongitude={ this.state.lastPosition.coords.longitude }
          onFindLocation={ this._findUserPosition }
          onAddLocation={ this._getMarkerLocation }
        />
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    // justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
    paddingTop: 20,
  },
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
  instructions: {
    textAlign: 'center',
    color: '#333333',
    marginBottom: 5,
  },
  locationListContainer: {
    flex: 1,
    alignSelf: 'stretch'
  },
  locationList: {
    alignSelf: 'stretch'
  },
  button: {
    backgroundColor: '#00998a',
    borderRadius: 5,
    marginTop: 20
  }
});

AppRegistry.registerComponent('reactNativeProject', () => reactNativeProject);
<|endoftext|>


<commit_before>jQuery($(document).on('turbolinks:load', function (){

    var minimized_elements = $('.monograph-metadata .description');

    minimized_elements.each(function(){
        // TODO: Threshold should probably be passed in as a parameter.
        var threshold = 500;
        if ($(this).text().length <= threshold) return;

        $md = $($.parseHTML($(this).html()));
        var m = $md.length;
        var n = 0;
        var text = "";
        var total = 0;
        $(this).html(text);
        for(n=0; n<m; n++){
          // Text nodes have a nodeType of 3
          text = ($md[n].nodeType == 3) ? $md[n].data : $md[n].innerText;
          total += text.length;
          if (total > threshold) break;
          $(this).append($md[n]);
        }

        var index = text.length - (total - threshold);
        while ((text.charAt(index) != ' ') && (index > 0)) index--;
        $clone = $($md[n]).clone();
        // Text nodes have a nodeType of 3
        if ($md[n].nodeType == 3) {
          $clone[0].textContent = text.slice(0,index);
          $md[n].textContent = text.slice(index,text.length);
        } else {
          $clone[0].innerHTML = text.slice(0,index);
          $md[n].innerHTML = text.slice(index,text.length);
        }
        $(this).append($clone[0]);
        $(this).append('... More');
        $less = $('');
        for(; n<m; n++){
          $less.append($md[n]);
        }
        $less.append(' Less');
        $(this).append($less);
    });

    $('a.more', minimized_elements).click(function(event){
        event.preventDefault();
        $(this).hide().prev().hide();
        $(this).next().show();
    });

    $('a.less', minimized_elements).click(function(event){
        event.preventDefault();
        $(this).parent().hide().prev().show().prev().show();
    });
}));

<commit_msg>Resolves #954: Monograph description teaser issues<commit_after>jQuery($(document).on('turbolinks:load', function (){

    var minimized_elements = $('.monograph-metadata .description');

    minimized_elements.each(function(){
        // TODO: Threshold should probably be passed in as a parameter.
        var threshold = 500;

        // Just return if content length less than threshold, nop
        if ($(this).text().length <= threshold) return;

        // Locate element to slice
        $md = $($.parseHTML($(this).html())); // extract elements from 'this'
        var m = $md.length; // number of elements
        var n = 0; // element to slice
        var content = ""; // element content buffer
        var length = 0; // running content length
        $(this).html(content); // start with no content in 'this'
        for(n=0; n<m; n++){
          // Text nodes have a nodeType of 3
          content = ($md[n].nodeType == 3) ? $md[n].data : $md[n].innerHTML;
          length += content.length;
          if (length > threshold) break;  // found element to slice, element n
          $(this).append($md[n]); // append element to 'this' content
        }

        // Determine index of where to slice element n
        var index = content.length - (length - threshold); // first guess
        var regexp = /<.+>.+<\/.+>/g;
        var buffer;
        while (buffer = regexp.exec(content)) {
          if (index < buffer.index) {
            break;
          }
          else if (index < regexp.lastIndex) {
            index = buffer.index;
            break;
          }
        }
        while ((content.charAt(index) != ' ') && (index > 0)) index--; // slice on space

        // Clone element n and truncate content at index
        $clone = $($md[n]).clone();
        // Text nodes have a nodeType of 3
        if ($md[n].nodeType == 3) {
          $clone[0].textContent = content.slice(0,index);
        } else {
          $clone[0].innerHTML = content.slice(0,index);
        }
        $clone[0].append(' ...');

        // Create less span and append truncated clone of element n
        $less = $('');
        $less.append($clone[0]);
        $(this).append($less);
        $(this).append('  More >>');

        // Create more span and append element n and the remaining elements
        $more = $('');
        for(; n<m; n++) {
          $more.append($md[n]);
        }
        $more.append(' << Less');
        $(this).append($more);
    });

    $('a.more', minimized_elements).click(function(event){
        event.preventDefault();
        $(this).hide().prev().hide();
        $(this).next().show();
    });

    $('a.less', minimized_elements).click(function(event){
        event.preventDefault();
        $(this).parent().hide().prev().show().prev().show();
    });
}));

<|endoftext|>


<commit_before>/* jslint browser: true */
/* jshint globalstrict: false */

var WUI_Dialog = new (function() {
    /***********************************************************
        Private section.

        Fields.
    ************************************************************/

    var _widget_list = {},

        _dragged_dialog = null,

        _touch_identifier = null,

        _drag_x = 0,
        _drag_y = 0,

        _class_name = {
            btn:           "wui-dialog-btn",
            btn_close:     "wui-dialog-close",
            minimize:      "wui-dialog-minimize",
            maximize:      "wui-dialog-maximize",
            header:        "wui-dialog-header",
            open:          "wui-dialog-open",
            draggable:     "wui-dialog-draggable"
        },

        _known_options = {
            title: "",

            width: "80%",
            height: "40%",

            open: true,

            closable: true,
            minimizable: false,
            draggable: false,

            halign: "none", // 'left', 'center', 'right' or 'none'
            valign: "none", // 'top', 'center', 'bottom' or 'none'

            top: "0px",
            bottom: "0px",
            left: "0px",
            right: "0px",

            use_event_listener: true
        };

    /***********************************************************
        Private section.

        Functions.
    ************************************************************/

    var _onCloseBtnClick = function (ev) {
        var dialog = ev.target.parentElement.parentElement;

        dialog.classList.remove(_class_name.open);
    };

    var _onMinimaxiBtnClick = function (ev) {
        var btn = ev.target,

            dialog = ev.target.parentElement.parentElement;

        btn.classList.toggle(_class_name.minimize);
        btn.classList.toggle(_class_name.maximize);

        dialog.classList.toggle("wui-dialog-minimized");
    };

    var _windowMouseMove = function (ev) {
        if(ev.preventDefault) {
            ev.preventDefault();
        }

        var x = ev.clientX,
            y = ev.clientY,

            touches = ev.changedTouches,

            touch = null,

            i,

            new_x, new_y,

            widget = _widget_list[_dragged_dialog.id];

        if (touches) {
            for (i = 0; i < touches.length; i += 1) {
                touch = touches[i];

                if (touch.identifier === _touch_identifier) {
                    x = touches[i].clientX;
                    y = touches[i].clientY;

                    break;
                }
            }
        }

        new_x = x - _drag_x;
        new_y = y - _drag_y;

        _dragged_dialog.style.left = new_x + 'px';
        _dragged_dialog.style.top  = new_y + 'px';

        if (widget.opts.halign === "right") {
            _dragged_dialog.style.right = new_x + 'px';
        } else if (widget.opts.halign === "center") {
            _dragged_dialog.style.right = -new_x + 'px';
        }

        if (widget.opts.valign === "bottom") {
            _dragged_dialog.style.bottom = new_y  + 'px';
        } else if (widget.opts.valign === "center") {
            _dragged_dialog.style.bottom = -new_y  + 'px';
        }
    };

    var _windowMouseUp = function (ev) {
        var touches = ev.changedTouches,

            touch = null,

            i;

        if (touches) {
            for (i = 0; i < touches.length; i += 1) {
                touch = touches[i];

                if (touch.identifier === _touch_identifier) {
                    _dragged_dialog = null;

                    document.body.style.cursor = "default";

                    window.removeEventListener('touchmove', _windowMouseMove, false);

                    break;
                }
            }
        } else {
            _dragged_dialog = null;

            document.body.style.cursor = "default";

            window.removeEventListener('mousemove', _windowMouseMove, false);
        }
    };

    var _onMouseDown = function (ev) {
        var z_index = 100,

            cz_index = 0,

            dialog = null,

            x = ev.clientX,
            y = ev.clientY,

            touches = ev.changedTouches;

        if(ev.preventDefault) {
            ev.preventDefault();
        }

        if (_dragged_dialog === null) {
            if (touches) {
                _touch_identifier = touches[0].identifier;

                x = touches[0].clientX;
                y = touches[0].clientY;
            } else if (ev.button !== 0) {
                return;
            }
        }

        _dragged_dialog = ev.target.parentElement;

        if (_dragged_dialog.classList.contains(_class_name.maximize) ||
           !_dragged_dialog.classList.contains(_class_name.draggable)) {
            return;
        }

        for (var i in _widget_list) {
            if (_widget_list.hasOwnProperty(i)) {
                dialog = _widget_list[i].dialog;

                if (!isNaN(dialog.style.zIndex)) {
                    cz_index = parseInt(dialog.style.zIndex, 10);

                    if (cz_index > z_index) {
                        z_index = cz_index;
                    }

                    if (cz_index > 100000) { // guess 100000 dialogs is safe
                        cz_index -= 100000;
                    }
                }
            }
        }

        _dragged_dialog.style.zIndex = z_index + 1;

        document.body.style.cursor = "move";

        _drag_x = x - parseInt(_dragged_dialog.style.left, 10);
        _drag_y = y - parseInt(_dragged_dialog.style.top,  10);

        window.addEventListener('mousemove', _windowMouseMove, false);
        window.addEventListener('touchmove', _windowMouseMove, false);
    };

    /***********************************************************
        Public section.

        Functions.
    ************************************************************/

    /**
     * Trigger an event manually.
     */
    this.triggerEvent = function (event, type) {
        var element = event.target,

            e_type = event.type;

        if (type !== undefined) {
            e_type = type;
        }

        if (e_type === "click") {
            if (element.classList.contains(_class_name.btn_close)) {
                _onCloseBtnClick(event);

                return true;
            } else if (element.classList.contains(_class_name.minimize) ||
                element.classList.contains(_class_name.maximize)) {
                _onMinimaxiBtnClick(event);

                return true;
            }
        } else if (e_type === "mousedown") {
            if (element.classList.contains(_class_name.header)) {
                _onMouseDown(event);

                return true;
            }
        }

        return false;
    };

    this.create = function (id, options) {
        var opts = {},

            key;

        if (_widget_list[id] !== undefined) {
            console.log("WUI_Dialog id '" + id + "' already created, aborting.");

            return;
        }

        for (key in _known_options) {
            if (_known_options.hasOwnProperty(key)) {
                opts[key] = _known_options[key];
            }
        }

        if (options !== undefined) {
            for (key in options) {
                if (options.hasOwnProperty(key)) {
                    if (_known_options[key] !== undefined) {
                        opts[key] = options[key];
                    }
                }
            }
        }

        var dialog = document.getElementById(id),

            header = document.createElement("div"),

            header_close_btn    = null,
            header_minimaxi_btn = null,
            header_title        = null;

        if (dialog === null) {
            if (typeof console !== "undefined") {
                console.log("WUI dialog create, unknow element with id \"" + id + "\".");
            }

            return;
        }

        var content = dialog.firstElementChild;

        if (content === null) {
            content = document.createElement("div");

            dialog.appendChild(content);
        }

        // set dialog style
        dialog.style.width  = opts.width;
        dialog.style.height = opts.height;

        if (opts.halign === "center") {
            dialog.style.left  = "0";
            dialog.style.right = "0";
        } else if (opts.halign === "right") {
            dialog.style.right = "0";
            dialog.style.left = "auto";
        } else if (opts.halign === "left") {
            dialog.style.left = "0";
        } else {
            dialog.style.left = opts.left;
            dialog.style.right = opts.right;
        }

        if (opts.valign === "center") {
            dialog.style.top    = "0";
            dialog.style.bottom = "0";
        } else if (opts.valign === "bottom") {
            dialog.style.top = "auto";
            dialog.style.bottom = "0";
        } else if (opts.valign === "top") {
            dialog.style.top = "0";
        } else {
            dialog.style.top = opts.top;
            dialog.style.bottom = opts.bottom;
        }

        dialog.classList.add("wui-dialog");

        content.classList.add("wui-dialog-content");

        // build the dialog header (btns and the title)
        header.className = _class_name.header;

        content.style.height = dialog.offsetHeight - 32 + "px";

        if (opts.title !== "") {
            header_title = document.createElement("div");

            header_title.className = "wui-dialog-title";
            header_title.innerHTML = opts.title;

            header.appendChild(header_title);
        }

        if (opts.open) {
            dialog.classList.add(_class_name.open);
        }

        if (opts.draggable) {
            dialog.classList.toggle(_class_name.draggable);

            if (opts.use_event_listener) {
                header.addEventListener("mousedown", _onMouseDown, false);
                header.addEventListener("touchstart", _onMouseDown, false);
            }

            // exception for this one
            window.addEventListener('mouseup', _windowMouseUp, false);
            window.addEventListener('touchend', _windowMouseUp, false);
        }

        if (opts.closable) {
            header_close_btn = document.createElement("div");
            header_close_btn.className = _class_name.btn + " " + _class_name.btn_close;

            if (opts.use_event_listener) {
                header_close_btn.addEventListener("click", _onCloseBtnClick, false);
            }

            header.appendChild(header_close_btn);
        }

        if (opts.minimizable) {
            header_minimaxi_btn = document.createElement("div");
            header_minimaxi_btn.className = _class_name.btn + " " + _class_name.minimize;

            if (opts.use_event_listener) {
                header_minimaxi_btn.addEventListener("click", _onMinimaxiBtnClick, false);
            }

            header.appendChild(header_minimaxi_btn);
        }

        // go!
        dialog.insertBefore(header, content);

        _widget_list[id] =  {
                                dialog: dialog,
                                minimized_id: -1,
                                bottom_pos: dialog.style.bottom,

                                opts: opts
                            };

        return id;
    };

    this.open = function (id) {
        if (_widget_list[id] === undefined) {
            if (typeof console !== "undefined") {
                console.log("Cannot open WUI dialog \"" + id + "\".");
            }

            return;
        }

        _widget_list[id].dialog.classList.add(_class_name.open);
    };
})();
<commit_msg>add touchstart event to dialog header buttons<commit_after>/* jslint browser: true */
/* jshint globalstrict: false */

var WUI_Dialog = new (function() {
    /***********************************************************
        Private section.

        Fields.
    ************************************************************/

    var _widget_list = {},

        _dragged_dialog = null,

        _touch_identifier = null,

        _drag_x = 0,
        _drag_y = 0,

        _class_name = {
            btn:           "wui-dialog-btn",
            btn_close:     "wui-dialog-close",
            minimize:      "wui-dialog-minimize",
            maximize:      "wui-dialog-maximize",
            header:        "wui-dialog-header",
            open:          "wui-dialog-open",
            draggable:     "wui-dialog-draggable"
        },

        _known_options = {
            title: "",

            width: "80%",
            height: "40%",

            open: true,

            closable: true,
            minimizable: false,
            draggable: false,

            halign: "none", // 'left', 'center', 'right' or 'none'
            valign: "none", // 'top', 'center', 'bottom' or 'none'

            top: "0px",
            bottom: "0px",
            left: "0px",
            right: "0px",

            use_event_listener: true
        };

    /***********************************************************
        Private section.

        Functions.
    ************************************************************/

    var _onCloseBtnClick = function (ev) {
        var dialog = ev.target.parentElement.parentElement;

        dialog.classList.remove(_class_name.open);
    };

    var _onMinimaxiBtnClick = function (ev) {
        var btn = ev.target,

            dialog = ev.target.parentElement.parentElement;

        btn.classList.toggle(_class_name.minimize);
        btn.classList.toggle(_class_name.maximize);

        dialog.classList.toggle("wui-dialog-minimized");
    };

    var _windowMouseMove = function (ev) {
        if(ev.preventDefault) {
            ev.preventDefault();
        }

        var x = ev.clientX,
            y = ev.clientY,

            touches = ev.changedTouches,

            touch = null,

            i,

            new_x, new_y,

            widget = _widget_list[_dragged_dialog.id];

        if (touches) {
            for (i = 0; i < touches.length; i += 1) {
                touch = touches[i];

                if (touch.identifier === _touch_identifier) {
                    x = touches[i].clientX;
                    y = touches[i].clientY;

                    break;
                }
            }
        }

        new_x = x - _drag_x;
        new_y = y - _drag_y;

        _dragged_dialog.style.left = new_x + 'px';
        _dragged_dialog.style.top  = new_y + 'px';

        if (widget.opts.halign === "right") {
            _dragged_dialog.style.right = new_x + 'px';
        } else if (widget.opts.halign === "center") {
            _dragged_dialog.style.right = -new_x + 'px';
        }

        if (widget.opts.valign === "bottom") {
            _dragged_dialog.style.bottom = new_y  + 'px';
        } else if (widget.opts.valign === "center") {
            _dragged_dialog.style.bottom = -new_y  + 'px';
        }
    };

    var _windowMouseUp = function (ev) {
        var touches = ev.changedTouches,

            touch = null,

            i;

        if (touches) {
            for (i = 0; i < touches.length; i += 1) {
                touch = touches[i];

                if (touch.identifier === _touch_identifier) {
                    _dragged_dialog = null;

                    document.body.style.cursor = "default";

                    window.removeEventListener('touchmove', _windowMouseMove, false);

                    break;
                }
            }
        } else {
            _dragged_dialog = null;

            document.body.style.cursor = "default";

            window.removeEventListener('mousemove', _windowMouseMove, false);
        }
    };

    var _onMouseDown = function (ev) {
        var z_index = 100,

            cz_index = 0,

            dialog = null,

            x = ev.clientX,
            y = ev.clientY,

            touches = ev.changedTouches;

        if(ev.preventDefault) {
            ev.preventDefault();
        }

        if (_dragged_dialog === null) {
            if (touches) {
                _touch_identifier = touches[0].identifier;

                x = touches[0].clientX;
                y = touches[0].clientY;
            } else if (ev.button !== 0) {
                return;
            }
        }

        _dragged_dialog = ev.target.parentElement;

        if (_dragged_dialog.classList.contains(_class_name.maximize) ||
           !_dragged_dialog.classList.contains(_class_name.draggable)) {
            return;
        }

        for (var i in _widget_list) {
            if (_widget_list.hasOwnProperty(i)) {
                dialog = _widget_list[i].dialog;

                if (!isNaN(dialog.style.zIndex)) {
                    cz_index = parseInt(dialog.style.zIndex, 10);

                    if (cz_index > z_index) {
                        z_index = cz_index;
                    }

                    if (cz_index > 100000) { // guess 100000 dialogs is safe
                        cz_index -= 100000;
                    }
                }
            }
        }

        _dragged_dialog.style.zIndex = z_index + 1;

        document.body.style.cursor = "move";

        _drag_x = x - parseInt(_dragged_dialog.style.left, 10);
        _drag_y = y - parseInt(_dragged_dialog.style.top,  10);

        window.addEventListener('mousemove', _windowMouseMove, false);
        window.addEventListener('touchmove', _windowMouseMove, false);
    };

    /***********************************************************
        Public section.

        Functions.
    ************************************************************/

    /**
     * Trigger an event manually.
     */
    this.triggerEvent = function (event, type) {
        var element = event.target,

            e_type = event.type;

        if (type !== undefined) {
            e_type = type;
        }

        if (e_type === "click") {
            if (element.classList.contains(_class_name.btn_close)) {
                _onCloseBtnClick(event);

                return true;
            } else if (element.classList.contains(_class_name.minimize) ||
                element.classList.contains(_class_name.maximize)) {
                _onMinimaxiBtnClick(event);

                return true;
            }
        } else if (e_type === "mousedown") {
            if (element.classList.contains(_class_name.header)) {
                _onMouseDown(event);

                return true;
            }
        }

        return false;
    };

    this.create = function (id, options) {
        var opts = {},

            key;

        if (_widget_list[id] !== undefined) {
            console.log("WUI_Dialog id '" + id + "' already created, aborting.");

            return;
        }

        for (key in _known_options) {
            if (_known_options.hasOwnProperty(key)) {
                opts[key] = _known_options[key];
            }
        }

        if (options !== undefined) {
            for (key in options) {
                if (options.hasOwnProperty(key)) {
                    if (_known_options[key] !== undefined) {
                        opts[key] = options[key];
                    }
                }
            }
        }

        var dialog = document.getElementById(id),

            header = document.createElement("div"),

            header_close_btn    = null,
            header_minimaxi_btn = null,
            header_title        = null;

        if (dialog === null) {
            if (typeof console !== "undefined") {
                console.log("WUI dialog create, unknow element with id \"" + id + "\".");
            }

            return;
        }

        var content = dialog.firstElementChild;

        if (content === null) {
            content = document.createElement("div");

            dialog.appendChild(content);
        }

        // set dialog style
        dialog.style.width  = opts.width;
        dialog.style.height = opts.height;

        if (opts.halign === "center") {
            dialog.style.left  = "0";
            dialog.style.right = "0";
        } else if (opts.halign === "right") {
            dialog.style.right = "0";
            dialog.style.left = "auto";
        } else if (opts.halign === "left") {
            dialog.style.left = "0";
        } else {
            dialog.style.left = opts.left;
            dialog.style.right = opts.right;
        }

        if (opts.valign === "center") {
            dialog.style.top    = "0";
            dialog.style.bottom = "0";
        } else if (opts.valign === "bottom") {
            dialog.style.top = "auto";
            dialog.style.bottom = "0";
        } else if (opts.valign === "top") {
            dialog.style.top = "0";
        } else {
            dialog.style.top = opts.top;
            dialog.style.bottom = opts.bottom;
        }

        dialog.classList.add("wui-dialog");

        content.classList.add("wui-dialog-content");

        // build the dialog header (btns and the title)
        header.className = _class_name.header;

        content.style.height = dialog.offsetHeight - 32 + "px";

        if (opts.title !== "") {
            header_title = document.createElement("div");

            header_title.className = "wui-dialog-title";
            header_title.innerHTML = opts.title;

            header.appendChild(header_title);
        }

        if (opts.open) {
            dialog.classList.add(_class_name.open);
        }

        if (opts.draggable) {
            dialog.classList.toggle(_class_name.draggable);

            if (opts.use_event_listener) {
                header.addEventListener("mousedown", _onMouseDown, false);
                header.addEventListener("touchstart", _onMouseDown, false);
            }

            // exception for this one
            window.addEventListener('mouseup', _windowMouseUp, false);
            window.addEventListener('touchend', _windowMouseUp, false);
        }

        if (opts.closable) {
            header_close_btn = document.createElement("div");
            header_close_btn.className = _class_name.btn + " " + _class_name.btn_close;

            if (opts.use_event_listener) {
                header_close_btn.addEventListener("click", _onCloseBtnClick, false);
                header_close_btn.addEventListener("touchstart", _onCloseBtnClick, false);
            }

            header.appendChild(header_close_btn);
        }

        if (opts.minimizable) {
            header_minimaxi_btn = document.createElement("div");
            header_minimaxi_btn.className = _class_name.btn + " " + _class_name.minimize;

            if (opts.use_event_listener) {
                header_minimaxi_btn.addEventListener("click", _onMinimaxiBtnClick, false);
                header_close_btn.addEventListener("touchstart", _onMinimaxiBtnClick, false);
            }

            header.appendChild(header_minimaxi_btn);
        }

        // go!
        dialog.insertBefore(header, content);

        _widget_list[id] =  {
                                dialog: dialog,
                                minimized_id: -1,
                                bottom_pos: dialog.style.bottom,

                                opts: opts
                            };

        return id;
    };

    this.open = function (id) {
        if (_widget_list[id] === undefined) {
            if (typeof console !== "undefined") {
                console.log("Cannot open WUI dialog \"" + id + "\".");
            }

            return;
        }

        _widget_list[id].dialog.classList.add(_class_name.open);
    };
})();
<|endoftext|>