<commit_before>var Job = Backbone.Model.extend({
    urlRoot: "/tilestore/job",
    idAttribute:"jobId",
    defaults: {
        id:null,
        jobId: null,
        name: null,
        description: null,
  		jobType: null,
        status: null,
        statusMessage: null,
        data: null,
		percentComplete: null,
        submitDate: null,
        startDate: null,
        endDate: null
    },
    initialize: function(){

    },

    parse:function(response){
//        alert("response = ");// + JSON.stringify(response) );

        var result = this.attributes;

        //alert(result);
        return result;
    }

    /*
    url:function() {
        result = this.urlRoot + "?";
        params = "";
        idx = 0;

        for (param in this.attributes) {
            if (params != "") {
                params += "&";
            }
            params += param + "=" + this.attributes[param];
        }

        return result + params;
    }
    */
});

var JobPageView = Backbone.View.extend({
    el:"#JobPageId",
    initialize:function(params){
        var thisPtr = this;
        this.timeoutId = null;
        this.timeoutInterval = 5000; // refresh every 5 seconds
        this.tableModel = params.tableModel;
        this.filter="";
        this.singleSelect=params.singleSelect;
        this.propertyGridId="#propertyGridId";
        this.jobTableId = "#jobTableId";
        this.usernameId = "#usernameId";
        this.usernameOpTypeId = "#usernameOpTypeId";
        this.applyFilterButtonId = "#applyFilterButtonId";
        this.resetButtonId = "#resetButtonId";
        this.usernameOpTypeId = "#usernameOpTypeId";
        this.jobStatusGroupCheckedList = "#jobStatusGroupId :checkbox:checked";
        this.jobStatusGroupCheckboxList = "#jobStatusGroupId :checkbox";
        this.removeJobId = "#removeId";
        this.donwnloadJobId = "#downloadId";
        this.reloadId = "#reloadId";
        this.cancelJobId = "#cancelJobId";
        this.confirmModal ='#confirmModal';
        this.deleteRow = '#deleteRow';
        this.errorModal = '#errorModal';
        this.errorModalMessage = '#errorModalMessage';
        this.urls=params.urls;
        $(this.applyFilterButtonId).click($.proxy(this.refresh, this));
        $(this.resetButtonId).click($.proxy(this.resetFilter, this));

        $(this.removeJobId).click($.proxy(this.removeJobClicked, this));
        $(this.donwnloadJobId).click($.proxy(this.downloadJobClicked, this));
        $(this.reloadId).click($.proxy(this.reload, this));
        $(this.cancelJobId).click($.proxy(this.cancelJobClicked, this));
        $.extend(true, this.tableModel,{
            url:params.url,
            idField:"jobId",
            singleSelect:thisPtr.singleSelect,
            loadFilter:function(param){
                if(param&&param.rows)
                {
                    thisPtr.resetTimerIfNeeded(param.rows);
                }
                else
                {
                    thisPtr.resetTimerIfNeeded();
                }
                return param;
             },
            frozenColumns: [[
                {field: 'ck', checkbox: true}
            ]]

        });
    },
    reload:function(){
        $(this.jobTableId).datagrid('clearSelections');
        $(this.jobTableId).datagrid('clearChecked');
        $(this.jobTableId).datagrid('reload');
    },
    downloadJobClicked:function(){
        var thisPtr = this;
        var rows = $(this.jobTableId).datagrid('getSelections');
        if(rows)
        {
            $(rows).each(function(idx, row){
                $.fileDownload(thisPtr.urls.download+"?jobId="+row.jobId)
                    .fail(function (message) { alert(JSON.stringify(message.responseText)); });
            })

        }
    },
    cancelJobClicked:function(){
        var thisPtr = this;
        var rows = $(this.jobTableId).datagrid('getSelections');
        if(rows)
        {
            $(rows).each(function(idx, row){
                $.post(thisPtr.urls.cancel+"?jobId="+row.jobId)
                    .fail(function (message) {alert(JSON.stringify(message.responseText)); });
            })
        }
    },
    removeSelectedRows:function()
    {
        var rows = $(this.jobTableId).datagrid('getSelections');
        if(rows)
        {
            var thisPtr = this;
            var nRows = rows.length;
            $(rows).each(function(idx,v){
                $.post(thisPtr.urls.remove,{id:v.id},function(result){
                })
                    .complete(function(result){
                        $(thisPtr.jobTableId).datagrid('clearSelections');
                        thisPtr.refresh();
                    })
            });
        }
    },
    removeJobClicked:function()
    {
        var thisPtr = this;
        var rows = $(this.jobTableId).datagrid('getSelections');
        var canRemove =true
        var errorMessage = "";
        if(rows)
        {
            $(rows).each(function(idx,v){
                if(v.status)
                {
                    var testV = v.status.toUpperCase();
                    if(testV == "RUNNING")
                    {
                        canRemove = false;
                        errorMessage = "Please cancel any running jobs before removing!"
                    }
                }
                if(!canRemove) return;
            });
        }
        if(canRemove)
        {
            var values = [];
            //$.messager.confirm('Confirm','Are you sure you want to remove and unregister this location?', function(r){
            //    if(r)
            //    {
            //
            //        thisPtr.removeSelectedRows();
            //    }
            //});

            $(this.confirmModal).modal('show');

            $(this.deleteRow).on('click', function(){
                console.log('thisPtr = ', thisPtr);
                thisPtr.removeSelectedRows();
            });

        }
        else
        {
            //$.messager.alert('Warning',errorMessage);
            $(this.errorModal).modal('show');
            $(this.errorModalMessage).html(errorMessage);
        }
     },
    resetFilter:function(){
        $(this.jobStatusGroupCheckboxList).each(function() {
            $(this).attr('checked', false);
        });

        $(this.usernameId).val("");
        this.refresh();
    },
    buildFilter:function(){
        var jobStatusFilter = "";
        var filter = "";
        $(this.jobStatusGroupCheckedList).each(function() {
            var temp = "("+$(this).attr('name') + "='"+$(this).attr('value')+"')";
            if(jobStatusFilter === "")
            {
                jobStatusFilter=temp;
            }
            else
            {
                jobStatusFilter = jobStatusFilter + " OR "+temp;
            }
        });
        if(jobStatusFilter!="")
        {
            filter =  "("+jobStatusFilter+")";
        }

        var usernameFilter = "";
        var usernameValue  =  $(this.usernameId).val();
        if((usernameValue != null) &&(usernameValue != ""))
        {
            var usernameOpType = "" + $(this.usernameOpTypeId).val();
            usernameOpType = usernameOpType.toLowerCase();
            if(usernameOpType!="")
            {
                usernameFilter = "(username";
                switch(usernameOpType)
                {
                    case "equals":
                        usernameFilter+="='"+usernameValue+"')"
                        break;
                    case "starts with":
                        usernameFilter+=" like '"+usernameValue+"%')"
                        break;
                    case "ends with":
                        usernameFilter+=" like '%"+usernameValue+"')"
                        break;
                    case "contains":
                        usernameFilter+=" like '%"+usernameValue+"%')"
                        break;
                }
                if(filter === "")
                {
                    filter=usernameFilter;
                }
                else
                {
                    filter += " AND "+usernameFilter;
                }
            }
         }

        return filter;
    },
    needToAutoUpdate:function(paramOverride){
        var result = false;
        var rows = null;

        if(paramOverride != null)
        {
            rows = paramOverride;
        }
        else
        {
            rows = $(this.jobTableId).datagrid("getRows");
        }
        if(rows)
        {
            $(rows).each(function(idx, v) {
                if(v.status === "RUNNING" || v.status==="READY")
                {
                    result = true;
                    return false; // break the for each loop
                }
            });

        }
        return result;
     },
    clearTimeout:function(){
        if(this.timeoutId)
        {
            clearTimeout(this.timeoutId);
            this.timeoutId = null;
        }
    },
    resetTimerIfNeeded:function(paramOverride){
        if(this.needToAutoUpdate(paramOverride))
        {
            this.clearTimeout();
            this.timeoutId = setTimeout($.proxy(this.refreshWatchList, this), this.timeoutInterval);
        }
    },
    refreshWatchList:function(){
        var thisPtr = this;
        var rows = $(this.jobTableId).datagrid("getRows");
        var queryList = [];
        $(rows).each(function(idx, v){
            if(v.status)
            {
                var status = v.status.toUpperCase();

                if((status === "RUNNING") ||
                   (status === "READY") )
                {
                    queryList.push(v.jobId);
                }
            }
        });
        if(queryList.length > 0)
        {
            this.currentGetQuery = $.ajax({
                url: "/tilestore/job/list?jobIds="+queryList.join(","),
                type: "GET",
                context: this,
                success:function(response)
                {
                    if(response&&response.rows)
                    {
                        $(response.rows).each(function(idx, v){
                            var idx = $(thisPtr.jobTableId).datagrid("getRowIndex", v.jobId);
                            $(thisPtr.jobTableId).datagrid("updateRow",{
                                index:idx,
                                row:v});
                        });
                    }
                    thisPtr.currentGetQuery = null;
                    thisPtr.resetTimerIfNeeded();
                },
                error:function(){
                }
            });

        }
    },
    refresh:function(){
        var filter = this.buildFilter();
        $('#jobTableId').datagrid('reload', {"filter":filter});
    },
    render:function(){
        var thisPtr = this;
        $(this.jobTableId).datagrid(thisPtr.tableModel);
        //$(this.propertyGridId).propertygrid();
        $(this.jobStatusGroupCheckboxList).change($.proxy(this.refresh, thisPtr));
        $(this.usernameId).change($.proxy(this.refresh, thisPtr));
        $(this.usernameOpTypeId).change($.proxy(this.refresh, thisPtr));
    }
});

var jobPage = null;
var JobPage = (function($, params){
    params.model =new Job({urlRoot:params.urls.base})
    jobPage = new JobPageView(params);

    jobPage.render();
    return jobPage;
});
<commit_msg>Added multiple id submit<commit_after>var Job = Backbone.Model.extend({
    urlRoot: "/tilestore/job",
    idAttribute:"jobId",
    defaults: {
        id:null,
        jobId: null,
        name: null,
        description: null,
  		jobType: null,
        status: null,
        statusMessage: null,
        data: null,
		percentComplete: null,
        submitDate: null,
        startDate: null,
        endDate: null
    },
    initialize: function(){

    },

    parse:function(response){
//        alert("response = ");// + JSON.stringify(response) );

        var result = this.attributes;

        //alert(result);
        return result;
    }

    /*
    url:function() {
        result = this.urlRoot + "?";
        params = "";
        idx = 0;

        for (param in this.attributes) {
            if (params != "") {
                params += "&";
            }
            params += param + "=" + this.attributes[param];
        }

        return result + params;
    }
    */
});

var JobPageView = Backbone.View.extend({
    el:"#JobPageId",
    initialize:function(params){
        var thisPtr = this;
        this.timeoutId = null;
        this.timeoutInterval = 5000; // refresh every 5 seconds
        this.tableModel = params.tableModel;
        this.filter="";
        this.singleSelect=params.singleSelect;
        this.propertyGridId="#propertyGridId";
        this.jobTableId = "#jobTableId";
        this.usernameId = "#usernameId";
        this.usernameOpTypeId = "#usernameOpTypeId";
        this.applyFilterButtonId = "#applyFilterButtonId";
        this.resetButtonId = "#resetButtonId";
        this.usernameOpTypeId = "#usernameOpTypeId";
        this.jobStatusGroupCheckedList = "#jobStatusGroupId :checkbox:checked";
        this.jobStatusGroupCheckboxList = "#jobStatusGroupId :checkbox";
        this.removeJobId = "#removeId";
        this.donwnloadJobId = "#downloadId";
        this.reloadId = "#reloadId";
        this.cancelJobId = "#cancelJobId";
        this.confirmModal ='#confirmModal';
        this.deleteRow = '#deleteRow';
        this.errorModal = '#errorModal';
        this.errorModalMessage = '#errorModalMessage';
        this.urls=params.urls;
        $(this.applyFilterButtonId).click($.proxy(this.refresh, this));
        $(this.resetButtonId).click($.proxy(this.resetFilter, this));

        $(this.removeJobId).click($.proxy(this.removeJobClicked, this));
        $(this.donwnloadJobId).click($.proxy(this.downloadJobClicked, this));
        $(this.reloadId).click($.proxy(this.reload, this));
        $(this.cancelJobId).click($.proxy(this.cancelJobClicked, this));
        $.extend(true, this.tableModel,{
            url:params.url,
            idField:"jobId",
            singleSelect:thisPtr.singleSelect,
            loadFilter:function(param){
                if(param&&param.rows)
                {
                    thisPtr.resetTimerIfNeeded(param.rows);
                }
                else
                {
                    thisPtr.resetTimerIfNeeded();
                }
                return param;
             },
            frozenColumns: [[
                {field: 'ck', checkbox: true}
            ]]

        });
    },
    reload:function(){
        $(this.jobTableId).datagrid('clearSelections');
        $(this.jobTableId).datagrid('clearChecked');
        $(this.jobTableId).datagrid('reload');
    },
    downloadJobClicked:function(){
        var thisPtr = this;
        var rows = $(this.jobTableId).datagrid('getSelections');
        if(rows)
        {
            $(rows).each(function(idx, row){
                $.fileDownload(thisPtr.urls.download+"?jobId="+row.jobId)
                    .fail(function (message) { alert(JSON.stringify(message.responseText)); });
            })

        }
    },
    cancelJobClicked:function(){
        var thisPtr = this;
        var rows = $(this.jobTableId).datagrid('getSelections');
        if(rows)
        {
            $(rows).each(function(idx, row){
                $.post(thisPtr.urls.cancel+"?jobId="+row.jobId)
                    .fail(function (message) {alert(JSON.stringify(message.responseText)); });
            })
        }
    },
    removeSelectedRows:function()
    {
        var rows = $(this.jobTableId).datagrid('getSelections');
        if(rows)
        {
            var thisPtr = this;
            var nRows = rows.length;
            var removeIdList = [];

            $(rows).each(function(idx,v) {
                removeIdList.push(v.id);
            });
            $.post(thisPtr.urls.remove,{id:removeIdList.join(",")},function(result){
            })
                .complete(function(result){
                    $(thisPtr.jobTableId).datagrid('clearSelections');
                    thisPtr.refresh();
                });
            //});
        }
    },
    removeJobClicked:function()
    {
        var thisPtr = this;
        var rows = $(this.jobTableId).datagrid('getSelections');
        var canRemove =true
        var errorMessage = "";
        if(rows)
        {
            $(rows).each(function(idx,v){
                if(v.status)
                {
                    var testV = v.status.toUpperCase();
                    if(testV == "RUNNING")
                    {
                        canRemove = false;
                        errorMessage = "Job is running and can't be removed!"
                    }
                }
                if(!canRemove) return;
            });
        }
        if(canRemove)
        {
            var values = [];
            //$.messager.confirm('Confirm','Are you sure you want to remove and unregister this location?', function(r){
            //    if(r)
            //    {
            //
            //        thisPtr.removeSelectedRows();
            //    }
            //});

            $(this.confirmModal).modal('show');

            $(this.deleteRow).on('click', function(){
                console.log('thisPtr = ', thisPtr);
                thisPtr.removeSelectedRows();
            });

        }
        else
        {
            //$.messager.alert('Warning',errorMessage);
            $(this.errorModal).modal('show');
            $(this.errorModalMessage).html(errorMessage);
        }
     },
    resetFilter:function(){
        $(this.jobStatusGroupCheckboxList).each(function() {
            $(this).attr('checked', false);
        });

        $(this.usernameId).val("");
        this.refresh();
    },
    buildFilter:function(){
        var jobStatusFilter = "";
        var filter = "";
        $(this.jobStatusGroupCheckedList).each(function() {
            var temp = "("+$(this).attr('name') + "='"+$(this).attr('value')+"')";
            if(jobStatusFilter === "")
            {
                jobStatusFilter=temp;
            }
            else
            {
                jobStatusFilter = jobStatusFilter + " OR "+temp;
            }
        });
        if(jobStatusFilter!="")
        {
            filter =  "("+jobStatusFilter+")";
        }

        var usernameFilter = "";
        var usernameValue  =  $(this.usernameId).val();
        if((usernameValue != null) &&(usernameValue != ""))
        {
            var usernameOpType = "" + $(this.usernameOpTypeId).val();
            usernameOpType = usernameOpType.toLowerCase();
            if(usernameOpType!="")
            {
                usernameFilter = "(username";
                switch(usernameOpType)
                {
                    case "equals":
                        usernameFilter+="='"+usernameValue+"')"
                        break;
                    case "starts with":
                        usernameFilter+=" like '"+usernameValue+"%')"
                        break;
                    case "ends with":
                        usernameFilter+=" like '%"+usernameValue+"')"
                        break;
                    case "contains":
                        usernameFilter+=" like '%"+usernameValue+"%')"
                        break;
                }
                if(filter === "")
                {
                    filter=usernameFilter;
                }
                else
                {
                    filter += " AND "+usernameFilter;
                }
            }
         }

        return filter;
    },
    needToAutoUpdate:function(paramOverride){
        var result = false;
        var rows = null;

        if(paramOverride != null)
        {
            rows = paramOverride;
        }
        else
        {
            rows = $(this.jobTableId).datagrid("getRows");
        }
        if(rows)
        {
            $(rows).each(function(idx, v) {
                if(v.status === "RUNNING" || v.status==="READY")
                {
                    result = true;
                    return false; // break the for each loop
                }
            });

        }
        return result;
     },
    clearTimeout:function(){
        if(this.timeoutId)
        {
            clearTimeout(this.timeoutId);
            this.timeoutId = null;
        }
    },
    resetTimerIfNeeded:function(paramOverride){
        if(this.needToAutoUpdate(paramOverride))
        {
            this.clearTimeout();
            this.timeoutId = setTimeout($.proxy(this.refreshWatchList, this), this.timeoutInterval);
        }
    },
    refreshWatchList:function(){
        var thisPtr = this;
        var rows = $(this.jobTableId).datagrid("getRows");
        var queryList = [];
        $(rows).each(function(idx, v){
            if(v.status)
            {
                var status = v.status.toUpperCase();

                if((status === "RUNNING") ||
                   (status === "READY") )
                {
                    queryList.push(v.jobId);
                }
            }
        });
        if(queryList.length > 0)
        {
            this.currentGetQuery = $.ajax({
                url: "/tilestore/job/list?jobIds="+queryList.join(","),
                type: "GET",
                context: this,
                success:function(response)
                {
                    if(response&&response.rows)
                    {
                        $(response.rows).each(function(idx, v){
                            var idx = $(thisPtr.jobTableId).datagrid("getRowIndex", v.jobId);
                            $(thisPtr.jobTableId).datagrid("updateRow",{
                                index:idx,
                                row:v});
                        });
                    }
                    thisPtr.currentGetQuery = null;
                    thisPtr.resetTimerIfNeeded();
                },
                error:function(){
                }
            });

        }
    },
    refresh:function(){
        var filter = this.buildFilter();
        $('#jobTableId').datagrid('reload', {"filter":filter});
    },
    render:function(){
        var thisPtr = this;
        $(this.jobTableId).datagrid(thisPtr.tableModel);
        //$(this.propertyGridId).propertygrid();
        $(this.jobStatusGroupCheckboxList).change($.proxy(this.refresh, thisPtr));
        $(this.usernameId).change($.proxy(this.refresh, thisPtr));
        $(this.usernameOpTypeId).change($.proxy(this.refresh, thisPtr));
    }
});

var jobPage = null;
var JobPage = (function($, params){
    params.model =new Job({urlRoot:params.urls.base})
    jobPage = new JobPageView(params);

    jobPage.render();
    return jobPage;
});
<|endoftext|>


<commit_before>'use strict'

import * as hazy from '../node_modules/hazy/src/hazy.js'
import protagonist from 'protagonist'
import hercule from 'hercule'

import _ from 'lodash'
import fs from 'fs'
import glob from 'glob'

/**
 * Parses, transcludes and compiles API blueprint files
 * and their associated fixtures. Allows compiled
 * API blueprints to be exported to the filesystem.
 */
export class Blueprint {

  /**
   * @param {String} markdown Valid API blueprint Markdown
   */
  constructor(markdown: String) {
    this.markdown = markdown

    const statics = ['parse', 'transclude', 'compile', 'fixtures']

    statics.forEach(method => {
      this[method] = () => Blueprint[method](this.markdown)
    })
  }

  /**
   * Parses markdown into a usable object
   *
   * @param {String} markdown Valid API blueprint markdown
   */
  static parse(markdown: String): Promise {
    return new Promise((resolve, reject) => {
      if (markdown) {
        protagonist.parse(markdown, (err, blueprint) => {
          if (!err) {
            resolve(blueprint)
          } else {
            reject(`Failed to parse file as valid API blueprint: ${err}`)
          }
        })
      } else {
        reject(`Markdown data required`)
      }
    })
  }

  /**
   * Scans API blueprint markdown for Hercule references and transcludes them
   *
   * @param {String} markdown
   * @returns {Promise}
   */
  static transclude(markdown: String): Promise {
    return new Promise((resolve, reject) => {
      if (markdown) {
        const transcludedMarkdown = hercule.transcludeString(markdown, _ => _)

        if (transcludedMarkdown) {
          resolve(transcludedMarkdown)
        } else {
          reject('Failed to parse Markdown for Hercule transclusions!')
        }
      } else {
        reject('Valid markdown required for transclusion')
      }
    })
  }

  /**
   * Compiles API blueprint markdown data by translcuding it and then parsing it for fixtures
   *
   * @param {String} markdown
   * @returns {Promise}
   */
  static compile(markdown: String): Promise {
    return Blueprint
      .transclude(markdown)
      .then(transMd => Blueprint.parse(transMd))
      .then(parseMd => Blueprint.fixtures(parseMd).then(fixtures => {
        return {fixtures, content: parseMd}
      }))
  }

  /**
   * Compiles and extracts fixtures from API blueprint markdown
   *
   * @param {String} markdown
   * @returns {Promise}
   */
  static fixtures(markdown: String): Promise {
    return new Promise((resolve, reject) => {
      let fixtures = []
      const haziedMarkdown = hazy.lang.process(markdown)
      const jsonStrMatches = haziedMarkdown.match(plainJson)

      jsonStrMatches.forEach(jsonStr => {
        try {
          const fixture = JSON.parse(jsonStr)

          fixtures.push(fixture)
        } catch (e) {
          if (e instanceof SyntaxError) {
            reject(`Found invalid JSON in API blueprint ${blueprint.filepath}: ${jsonStr}\n\n${e}`)
          } else {
            reject(`Failed to parse JSON fixtures in API blueprint: ${e}`)
          }
        }
      })

      resolve(fixtures)
    })
  }

  /**
   * Parses and compiles an API blueprint from the file system
   *
   * @param {String} filepath
   * @returns {Promise}
   */
  static fromFile(filepath: String): Promise {
    return new Promise((resolve, reject) => {
      if (filepath) {
        fs.readFile(__dirname + '/' + filepath, 'utf-8', (err, data) => {
          if (!err) {
            resolve(new Blueprint(data).compile())
          } else {
            reject(`Failed to read file: ${error}`)
          }
        })
      } else {
        reject(`File to read file, filepath required`)
      }
    })
  }

  /**
   * Exports API blueprint as either a static html or apib file
   * to the provided file path.
   *
   * @param {String} filepath
   * @returns {Promise}
   */
  toFile(filepath: String): Promise {
    return new Promise((resolve, reject) => {
      const extension = filepath.match(/\.(.*?)$/)

      if (extension) {
        this.compile()
          .then(compiled   => this.marshall(compiled.content, extension[0]))
          .then(marshalled => {
            fs.writeFile(destination, marshalled, 'utf-8', err => {
              if (!err) {
                resolve(content)
              } else {
                reject(`An error occured while saving file: ${err}`)
              }
            })
          })
      } else {
        reject(`File destinations must contain an extension (.apib or .html)`)
      }
    })
  }

  /**
   * Attempts to marshall API blueprint content into a specific type.
   *
   * @param {String} markdown
   * @param {String} filetype 'apib' or 'html'
   * @returns {Promise}
   */
  marshall(markdown: String, filetype: String): Promise {
    return new Promise((resolve, reject) => {
      if (filetype === 'apib') {
        resolve(markdown)
      } else if (filetype === 'html') {
        resolve(null) // TODO
      } else {
        reject(`Unknown filetype provided: ${filetype}`)
      }
    })
  }

}

/**
 * Reads in valid API blueprint(s) and compiles them
 *
 * @param {Array|String} blueprints
 * @returns {Promise}
 */
export function load(blueprints): Promise {
  return new Promise((resolve, reject) => {
    if (blueprints instanceof Array) {
      return Promise.all(
        blueprints.map(bp => new Blueprint(bp).compile())
      )
    } else if (blueprints instanceof String) {
      return new Blueprint(blueprints).compile()
    } else {
      reject('Documents must be represented as a String or Array')
    }
  })
}

/**
 * A janky regex for finding "JSON" objects in markdown.
 */
export const plainJson = /\{(.*?)\}/gm

// export class LoadError  extends Error {}
// export class ParseError extends Error {}
<commit_msg>:art: load from filesystem, read from string or array<commit_after>'use strict'

import * as hazy from '../node_modules/hazy/src/hazy.js'
import protagonist from 'protagonist'
import hercule from 'hercule'

import _ from 'lodash'
import fs from 'fs'
import glob from 'glob'

/**
 * Parses, transcludes and compiles API blueprint files
 * and their associated fixtures. Allows compiled
 * API blueprints to be exported to the filesystem.
 */
export class Blueprint {

  /**
   * @param {String} markdown Valid API blueprint markdown
   */
  constructor(markdown: String, hazy?: Object = hazy) {
    this.markdown = markdown
    this.hazy     = hazy

    const statics = ['parse', 'transclude', 'compile', 'fixtures']

    statics.forEach(method => {
      this[method] = () => Blueprint[method](this.markdown)
    })
  }

  /**
   * Parses markdown into a usable object
   *
   * @param {String} markdown Valid API blueprint markdown
   */
  static parse(markdown: String): Promise {
    return new Promise((resolve, reject) => {
      if (markdown) {
        protagonist.parse(markdown, (err, blueprint) => {
          if (!err) {
            resolve(blueprint)
          } else {
            reject(`Failed to parse file as valid API blueprint: ${err}`)
          }
        })
      } else {
        reject(`Markdown data required`)
      }
    })
  }

  /**
   * Scans API blueprint markdown for Hercule references and transcludes them
   *
   * @param {String} markdown
   * @returns {Promise}
   */
  static transclude(markdown: String): Promise {
    return new Promise((resolve, reject) => {
      if (markdown) {
        const transcludedMarkdown = hercule.transcludeString(markdown, _ => _)

        if (transcludedMarkdown) {
          resolve(transcludedMarkdown)
        } else {
          reject('Failed to parse markdown for Hercule transclusions!')
        }
      } else {
        reject('Valid markdown required for transclusion')
      }
    })
  }

  /**
   * Compiles API blueprint markdown data by translcuding it and then parsing it for fixtures
   *
   * @param {String} markdown
   * @returns {Promise}
   */
  static compile(markdown: String): Promise {
    return Blueprint
      .transclude(markdown)
      .then(transMd => Blueprint.parse(transMd))
      .then(parseMd => Blueprint.fixtures(parseMd).then(fixtures => {
        return {fixtures, content: parseMd}
      }))
  }

  /**
   * Compiles and extracts fixtures from API blueprint markdown
   *
   * @param {String} markdown
   * @returns {Promise}
   */
  static fixtures(markdown: String): Promise {
    return new Promise((resolve, reject) => {
      let fixtures = []
      const haziedMarkdown = hazy.lang.process(markdown)
      const jsonStrMatches = haziedMarkdown.match(plainJson)

      jsonStrMatches.forEach(jsonStr => {
        try {
          const fixture = JSON.parse(jsonStr)

          fixtures.push(fixture)
        } catch (e) {
          if (e instanceof SyntaxError) {
            reject(`Found invalid JSON in API blueprint ${blueprint.filepath}: ${jsonStr}\n\n${e}`)
          } else {
            reject(`Failed to parse JSON fixtures in API blueprint: ${e}`)
          }
        }
      })

      resolve(fixtures)
    })
  }

  /**
   * Parses and compiles an API blueprint from the file system
   *
   * @param {String} filepath
   * @returns {Promise}
   */
  static fromFile(filepath: String): Promise {
    return new Promise((resolve, reject) => {
      if (filepath) {
        fs.readFile(__dirname + '/' + filepath, 'utf-8', (err, data) => {
          if (!err) {
            resolve(new Blueprint(data).compile())
          } else {
            reject(`Failed to read file: ${error}`)
          }
        })
      } else {
        reject(`Failed to read file, filepath required`)
      }
    })
  }

  /**
   * Exports API blueprint as either a static html or apib file
   * to the provided file path.
   *
   * @param {String} filepath
   * @returns {Promise}
   */
  toFile(fixtures, filepath: String): Promise {
    return new Promise((resolve, reject) => {
      const extension = filepath.match(/\.(.*?)$/)

      if (extension) {
        this.read(fixtures)
          .then(compiled   => this.marshall(compiled.content, extension[0]))
          .then(marshalled => {
            fs.writeFile(destination, marshalled, 'utf-8', err => {
              if (!err) {
                resolve(content)
              } else {
                reject(`An error occured while saving file: ${err}`)
              }
            })
          })
      } else {
        reject(`File destinations must contain an extension (.apib or .html)`)
      }
    })
  }

  /**
   * Attempts to marshall API blueprint content into a specific type.
   *
   * @param {String} markdown
   * @param {String} filetype 'apib' or 'html'
   * @returns {Promise}
   */
  marshall(markdown: String, filetype: String): Promise {
    return new Promise((resolve, reject) => {
      if (filetype === 'apib') {
        resolve(markdown)
      } else if (filetype === 'html') {
        resolve(null) // TODO
      } else {
        reject(`Unsupported filetype: ${filetype}`)
      }
    })
  }

}

/**
 * Reads in valid API blueprint(s) from Strings or
 * Arrays of Strings and compiles them
 *
 * @param {Array|String} blueprints
 * @returns {Promise}
 */
export function read(blueprints): Promise {
  return new Promise((resolve, reject) => {
    if (blueprints instanceof Array) {
      return Promise.all(
        blueprints.map(bp => new Blueprint(bp).compile())
      )
    } else if (blueprints instanceof String) {
      return new Blueprint(blueprints).compile()
    } else {
      reject('Documents must be represented as a String or Array')
    }
  })
}

/**
 * Globs and reads in valid API blueprint(s) from the filesystem and compiles them
 *
 * @param {Array|String} blueprints
 * @returns {Promise}
 */
export function load(pattern, options): Promise {
  return new Promise((resolve, reject) => {
    glob(pattern, options, (err, files) => {
      if (!err) {
        return Promise.all(
          files.map(file => new Blueprint(file).compile())
        )
      } else {
        reject(`Failed to load file: ${err}`)
      }
    })
  })
}

/**
 * A janky regex for finding "JSON" objects in markdown.
 */
export const plainJson = /\{(.*?)\}/gm

// export class LoadError  extends Error {}
// export class ParseError extends Error {}
<|endoftext|>


<commit_before>/*#ifndef(UMD)*/
"use strict";
/*exported _GPF_HOST_BROWSER*/ // gpf.HOST_BROWSER
/*exported _GPF_HOST_NODEJS*/ // gpf.HOST_NODEJS
/*exported _GPF_HOST_PHANTOMJS*/ // gpf.HOST_PHANTOMJS
/*exported _GPF_HOST_RHINO*/ // gpf.HOST_RHINO
/*exported _GPF_HOST_UNKNOWN*/ // gpf.HOST_UNKNOWN
/*exported _GPF_HOST_WSCRIPT*/ // gpf.HOST_WSCRIPT
/*exported _gpfDosPath*/ // DOS-like path
/*exported _gpfEmptyFunc*/ // An empty function
/*exported _gpfExit*/ // Exit function
/*exported _gpfHost*/ // Host type
/*exported _gpfIgnore*/ // Helper to remove unused parameter warning
/*exported _gpfInBrowser*/ // The current host is a browser like
/*exported _gpfInNode*/ // The current host is a nodeJS like
/*exported _gpfMainContext*/ // Main context object
/*exported _gpfMsFSO*/ // Scripting.FileSystemObject activeX
/*exported _gpfNodeFs*/ // Node require("fs")
/*exported _gpfNodePath*/ // Node require("path")
/*exported _gpfResolveScope*/ // Translate the parameter into a valid scope
/*exported _gpfVersion*/ // GPF version
/*exported _gpfWebDocument*/ // Browser document object
/*exported _gpfWebHead*/ // Browser head tag
/*exported _gpfWebWindow*/ // Browser window object
/*eslint-disable no-unused-vars*/
/*#endif*/

 /*eslint-disable no-invalid-this, no-useless-call*/ // this is used to grab the global context

// An empty function
function _gpfEmptyFunc () {}

var
    // GPF version
    _gpfVersion = "0.1.5-alpha",

    // Host type constants
    _GPF_HOST_BROWSER               = "browser",
    /*jshint browser: true*/
    /*eslint-env browser*/

    _GPF_HOST_NODEJS                = "nodejs",
    /*jshint node: true*/
    /*eslint-env node*/

    _GPF_HOST_PHANTOMJS             = "phantomjs",
    /*jshint phantom: true*/
    /*eslint-env phantomjs*/

    _GPF_HOST_RHINO                 = "rhino",
    /*jshint rhino: true*/
    /*eslint-env rhino*/

    _GPF_HOST_UNKNOWN               = "unknown",

    _GPF_HOST_WSCRIPT               = "wscript",
    /*jshint wsh: true*/
    /*eslint-env wsh*/

    // Host type, see _GPF_HOST_xxx
    _gpfHost = _GPF_HOST_UNKNOWN,

    // Indicates that paths are DOS-like (i.e. case insensitive with /)
    _gpfDosPath = false,

    /*jshint -W040*/ // This is the common way to get the global context
    // Main context object
    _gpfMainContext = this, //eslint-disable-line consistent-this
    /*jshint +W040*/

    /**
     * Helper to ignore unused parameter
     *
     * @param {*} param
     */
    /*gpf:nop*/ _gpfIgnore = _gpfEmptyFunc,

    /**
     * Exit function
     *
     * @param {Number} code
     */
    _gpfExit = _gpfEmptyFunc,

    // The current host is a nodeJS like
    _gpfInNode = false,

    // The current host is a browser like
    _gpfInBrowser = false,

    /**
     * Browser window object
     *
     * @type {Object}
     */
    _gpfWebWindow,

    /**
     * Browser document object
     *
     * @type {Object}
     */
    _gpfWebDocument,

    /**
     * Browser head tag
     *
     * @type {Object}
     */
    _gpfWebHead,

    /**
     * Scripting.FileSystemObject activeX
     *
     * @type {Object}
     */
    _gpfMsFSO,

    /**
     * Node require("fs")
     *
     * @type {Object}
     */
    _gpfNodeFs,

    /**
     * Node require("path")
     *
     * @type {Object}
     */
    _gpfNodePath;

/**
 * Translate the parameter into a valid scope
 *
 * @param {*} scope
 */
function _gpfResolveScope (scope) {
    if (null === scope || "object" !== typeof scope) {
        return _gpfMainContext;
    }
    return scope;
}

/*#ifdef(DEBUG)*/

_gpfVersion += "-debug";

/*#endif*/

/*#ifndef(UMD)*/

var
    /**
     * Asynchronous load function
     *
     * @param {String} srcFileName
     * @param {Function} callback
     * - {String} [content=undefined] content if provided, the content is concatenated to be evaluated in the end.
     * If not provided, it is assumed that the source was evaluated in the global context
     */
    _gpfAsyncLoadForBoot,
    /**
     * Synchronous read function
     *
     * @param {String} srcFileName
     * @return {String} content of the srcFileName
     */
    _gpfSyncReadForBoot;

/*#endif*/

/* Host detection */
/* istanbul ignore next */

// Microsoft cscript / wscript
if ("undefined" !== typeof WScript) {
    /*eslint-disable new-cap*/

    _gpfHost = _GPF_HOST_WSCRIPT;
    _gpfDosPath = true;
    _gpfMainContext = function () {
        return this;
    }.call(null);
    _gpfExit = function (code) {
        WScript.Quit(code);
    };

    // Define console APIs
    _gpfMainContext.console = {
        log: function (t) {
            WScript.Echo("    " + t);
        },
        info: function (t) {
            WScript.Echo("[?] " + t);
        },
        warn: function (t) {
            WScript.Echo("/!\\ " + t);
        },
        error: function (t) {
            WScript.Echo("(X) " + t);
        }
    };

/*#ifndef(UMD)*/

    _gpfMsFSO = new ActiveXObject("Scripting.FileSystemObject");

    _gpfSyncReadForBoot = function (srcFileName) {
        var srcFile = _gpfMsFSO.OpenTextFile(srcFileName),
            srcContent = srcFile.ReadAll();
        srcFile.Close();
        return srcContent;
    };

/*#endif*/

    /*eslint-enable new-cap*/
} else if ("undefined" !== typeof print && "undefined" !== typeof java) {
    _gpfHost = _GPF_HOST_RHINO;
    _gpfDosPath = false;
    _gpfMainContext = function () {
        return this;
    }.call(null);
    _gpfExit = function (code) {
        java.lang.System.exit(code);
    };

    // Define console APIs
    _gpfMainContext.console = {
        log: function (t) {
            print("    " + t);
        },
        info: function (t) {
            print("[?] " + t);
        },
        warn: function (t) {
            print("/!\\ " + t);
        },
        error: function (t) {
            print("(X) " + t);
        }
    };

/*#ifndef(UMD)*/

    _gpfSyncReadForBoot = readFile;

/*#endif*/

// PhantomJS
} else if ("undefined" !== typeof phantom && phantom.version) {
    _gpfHost = _GPF_HOST_PHANTOMJS;
    _gpfDosPath = require("fs").separator === "\\";
    _gpfMainContext = window;
    _gpfInNode = true;
    _gpfInBrowser = true;
    _gpfExit = phantom.exit;

/*#ifndef(UMD)*/

    _gpfNodeFs =  require("fs");

    _gpfSyncReadForBoot = function (srcFileName) {
        return _gpfNodeFs.read(srcFileName);
    };

/*#endif*/

// Nodejs
} else if ("undefined" !== typeof module && module.exports) {
    _gpfHost = _GPF_HOST_NODEJS;
    _gpfNodePath = require("path");
    _gpfDosPath = _gpfNodePath.sep === "\\";
    _gpfMainContext = global;
    _gpfInNode = true;
    _gpfExit = process.exit;

/*#ifndef(UMD)*/

    /*eslint-disable no-sync*/ // Simpler this way

    _gpfNodeFs =  require("fs");

    _gpfSyncReadForBoot = function (srcFileName) {
        return _gpfNodeFs.readFileSync(srcFileName).toString();
    };

/*#endif*/

// Browser
} else if ("undefined" !== typeof window) {
    _gpfHost = _GPF_HOST_BROWSER;
    _gpfMainContext = window;
    _gpfInBrowser = true;
    _gpfExit = _gpfEmptyFunc;
    _gpfWebWindow = window;
    _gpfWebDocument = document;
    _gpfWebHead = _gpfWebDocument.getElementsByTagName("head")[0] || _gpfWebDocument.documentElement;

/*#ifndef(UMD)*/

    _gpfAsyncLoadForBoot = function (srcFileName, callback) {
        if (gpf.web.include) {
            gpf.web.include(srcFileName, function (event) {
                if (gpf.events.EVENT_ERROR === event.type) {
                    console.error(event.get("error").message);
                } else {
                    callback();
                }
            });
            return;
        }
        var script = _gpfWebDocument.createElement("script");
        script.language = "javascript";
        script.src = srcFileName;
        script = _gpfWebHead.insertBefore(script, _gpfWebHead.firstChild);
        function testSymbol (symbol) {
            try {
                /*jshint -W054*/
                return new Function("return " + symbol)(); //eslint-disable-line no-new-func
                /*jshint +W054*/
            } catch (e) {
                return undefined;
            }
        }
        var source = srcFileName.split("/").pop().split(".")[0],
            symbol = {
                "sources":          "gpf.sources",
                "assert":           "gpf.assert",
                "noconflict":       "gpf.noConflict",
                "arraylike":        "gpf.isArrayLike",
                "foreach":          "gpf.forEach",
                "extend":           "gpf.extend",
                "factory":          "gpf.internals._gpfGenericFactory",
                "array":            "_gpfCompatibility.Array",
                "date":             "_gpfCompatibility.Date",
                "function":         "_gpfCompatibility.Function",
                "object":           "_gpfCompatibility.Object",
                "string":           "_gpfCompatibility.String",
                "promise":          "gpf.Promise",
                "compatibility":    "gpf.internals._gpfCompatibility",
                "context":          "gpf.context",
                "constants":        "gpf.HOST_UNKNOWN",
                "events":           "gpf.events",
                "include":          "gpf.web.include"
            }[source];
        if (undefined === symbol) {
            console.error("No symbol to test for: " + source);
        }
        function wait () {
            if (undefined === testSymbol(symbol)) {
                // Use an aggressive setting as it will be used only for the non UMD version
                setTimeout(wait, 0);
            } else {
                _gpfWebHead.removeChild(script);
                callback();
            }
        }
        wait();
    };

/*#endif*/

}

/*#ifndef(UMD)*/

_gpfMainContext.gpf = {
    internals: {} // To support testable internals
};

/*#endif*/

// Install host specifics (if any)
/* istanbul ignore else */ // Because tested with NodeJS
if (_gpfInNode) {
    gpf.node = {};
}
// Some web-related tools will be configured even if not in a browser, declare the namespace now
gpf.web = {};

// Returns the current version
gpf.version = function () {
    return _gpfVersion;
};

/**
 * Returns a string identifying the detected host
 *
 * @return {String}
 * - gpf.HOST_WSCRIPT for cscript and wscript
 * - gpf.HOST_NODEJS for nodejs
 * - gpf.HOST_PHANTOMJS for phantomjs
 * - gpf.HOST_BROWSER for any browser
 * - gpf.HOST_UNKNOWN if not detected
 */
gpf.host = function () {
    return _gpfHost;
};

/*#ifndef(UMD)*/

var
    // Set to true once all sources of GPF are loaded
    _gpfLoaded = false,

    /**
     * List of functions to call on load
     *
     * @type {Function[]}
     */
    _gpfLoadCallbacks = [];

// Callback used once all sources are loaded, invoke the callbacks
function _gpfFinishLoading () {
    _gpfLoaded = true;
    while (_gpfLoadCallbacks.length) {
        /* istanbul ignore next */ // Not used within NodeJS (mostly browser only)
        _gpfLoadCallbacks.shift()();
    }
}

/* istanbul ignore next */ // Not used within NodeJS (mostly browser only)
/**
 * Test if GPF is loaded
 *
 * @param {Function} [callback=undefined] callback This callback is invoked when GPF is loaded (may be immediately if
 * already loaded)
 * @return {Boolean} True if GPF is fully loaded, false otherwise
 */
gpf.loaded = function (callback) {
    if (callback) {
        if (_gpfLoaded) {
            callback();
        } else {
            _gpfLoadCallbacks.push(callback);
        }
    }
    return _gpfLoaded;
};

/* istanbul ignore if */ // Because tested in DEBUG
if (!gpf.loaded) {

/*#else*/

    gpf.loaded = function (callback) {
        if (callback) {
            callback();
        }
        return true;
    };

/*#endif*/

/*#ifndef(UMD)*/

}

/*#endif*/

/*#ifndef(UMD)*/

/**
 * Loading sources occurs here for the non UMD version.
 * UMD versions (debug / release) will have everything concatenated.
 */

/* istanbul ignore next */ // Handled the right way with NodeJS
// gpfSourcesPath - if defined - gives the relative path to sources
if ("undefined" === typeof gpfSourcesPath) {
    _gpfMainContext.gpfSourcesPath = "";
} else {
    var pathSep;
    if (_gpfDosPath) {
        pathSep = "\\";
    } else {
        pathSep = "/";
    }
    if (gpfSourcesPath.charAt(gpfSourcesPath.length - 1) !== pathSep) {
        gpfSourcesPath += pathSep;
    }
}

/* istanbul ignore else */ // Should not happen
if (_gpfSyncReadForBoot) {
    _gpfAsyncLoadForBoot = function (name, callback) {
        callback(_gpfSyncReadForBoot(name));
    };
} else if (undefined === _gpfAsyncLoadForBoot) {
    throw new Error("A method must be defined to load sources");
}

_gpfAsyncLoadForBoot(gpfSourcesPath + "sources.js", function (sourcesContent) {
    /* istanbul ignore else */ // Only browser loads content immediately
    if (sourcesContent) {
        /*jslint evil: true*/
        eval(sourcesContent); //eslint-disable-line no-eval
        /*jslint evil: false*/
    }
    var sources = gpf.sources(),
        allContent = [],
        idx = 0;

    function next (content) {
        /* istanbul ignore else */ // Only browser loads content immediately
        if (content) {
            allContent.push(content);
        }
        var src = sources[idx++];
        if (src) {
            _gpfAsyncLoadForBoot(gpfSourcesPath + src + ".js", next);
        } else {
            /* istanbul ignore else */ // Only browser loads content immediately
            if (allContent.length) {
                /*jslint evil: true*/
                eval(allContent.join("\r\n")); //eslint-disable-line no-eval
                /*jslint evil: false*/
            }
            _gpfFinishLoading();
        }
    }

    next();
});

/*#endif*/
<commit_msg>Synchronous loading for the browser<commit_after>/*#ifndef(UMD)*/
"use strict";
/*exported _GPF_HOST_BROWSER*/ // gpf.HOST_BROWSER
/*exported _GPF_HOST_NODEJS*/ // gpf.HOST_NODEJS
/*exported _GPF_HOST_PHANTOMJS*/ // gpf.HOST_PHANTOMJS
/*exported _GPF_HOST_RHINO*/ // gpf.HOST_RHINO
/*exported _GPF_HOST_UNKNOWN*/ // gpf.HOST_UNKNOWN
/*exported _GPF_HOST_WSCRIPT*/ // gpf.HOST_WSCRIPT
/*exported _gpfDosPath*/ // DOS-like path
/*exported _gpfEmptyFunc*/ // An empty function
/*exported _gpfExit*/ // Exit function
/*exported _gpfHost*/ // Host type
/*exported _gpfIgnore*/ // Helper to remove unused parameter warning
/*exported _gpfInBrowser*/ // The current host is a browser like
/*exported _gpfInNode*/ // The current host is a nodeJS like
/*exported _gpfMainContext*/ // Main context object
/*exported _gpfMsFSO*/ // Scripting.FileSystemObject activeX
/*exported _gpfNodeFs*/ // Node require("fs")
/*exported _gpfNodePath*/ // Node require("path")
/*exported _gpfResolveScope*/ // Translate the parameter into a valid scope
/*exported _gpfVersion*/ // GPF version
/*exported _gpfWebDocument*/ // Browser document object
/*exported _gpfWebHead*/ // Browser head tag
/*exported _gpfWebWindow*/ // Browser window object
/*eslint-disable no-unused-vars*/
/*#endif*/

 /*eslint-disable no-invalid-this, no-useless-call*/ // this is used to grab the global context

// An empty function
function _gpfEmptyFunc () {}

var
    // GPF version
    _gpfVersion = "0.1.5-alpha",

    // Host type constants
    _GPF_HOST_BROWSER               = "browser",
    /*jshint browser: true*/
    /*eslint-env browser*/

    _GPF_HOST_NODEJS                = "nodejs",
    /*jshint node: true*/
    /*eslint-env node*/

    _GPF_HOST_PHANTOMJS             = "phantomjs",
    /*jshint phantom: true*/
    /*eslint-env phantomjs*/

    _GPF_HOST_RHINO                 = "rhino",
    /*jshint rhino: true*/
    /*eslint-env rhino*/

    _GPF_HOST_UNKNOWN               = "unknown",

    _GPF_HOST_WSCRIPT               = "wscript",
    /*jshint wsh: true*/
    /*eslint-env wsh*/

    // Host type, see _GPF_HOST_xxx
    _gpfHost = _GPF_HOST_UNKNOWN,

    // Indicates that paths are DOS-like (i.e. case insensitive with /)
    _gpfDosPath = false,

    /*jshint -W040*/ // This is the common way to get the global context
    // Main context object
    _gpfMainContext = this, //eslint-disable-line consistent-this
    /*jshint +W040*/

    /**
     * Helper to ignore unused parameter
     *
     * @param {*} param
     */
    /*gpf:nop*/ _gpfIgnore = _gpfEmptyFunc,

    /**
     * Exit function
     *
     * @param {Number} code
     */
    _gpfExit = _gpfEmptyFunc,

    // The current host is a nodeJS like
    _gpfInNode = false,

    // The current host is a browser like
    _gpfInBrowser = false,

    /**
     * Browser window object
     *
     * @type {Object}
     */
    _gpfWebWindow,

    /**
     * Browser document object
     *
     * @type {Object}
     */
    _gpfWebDocument,

    /**
     * Browser head tag
     *
     * @type {Object}
     */
    _gpfWebHead,

    /**
     * Scripting.FileSystemObject activeX
     *
     * @type {Object}
     */
    _gpfMsFSO,

    /**
     * Node require("fs")
     *
     * @type {Object}
     */
    _gpfNodeFs,

    /**
     * Node require("path")
     *
     * @type {Object}
     */
    _gpfNodePath;

/**
 * Translate the parameter into a valid scope
 *
 * @param {*} scope
 */
function _gpfResolveScope (scope) {
    if (null === scope || "object" !== typeof scope) {
        return _gpfMainContext;
    }
    return scope;
}

/*#ifdef(DEBUG)*/

_gpfVersion += "-debug";

/*#endif*/

/*#ifndef(UMD)*/

/**
 * Synchronous read function
 *
 * @param {String} srcFileName
 * @return {String} content of the srcFileName
 */
var _gpfSyncReadForBoot;

/*#endif*/

/* Host detection */
/* istanbul ignore next */

// Microsoft cscript / wscript
if ("undefined" !== typeof WScript) {
    /*eslint-disable new-cap*/

    _gpfHost = _GPF_HOST_WSCRIPT;
    _gpfDosPath = true;
    _gpfMainContext = function () {
        return this;
    }.call(null);
    _gpfExit = function (code) {
        WScript.Quit(code);
    };

    // Define console APIs
    _gpfMainContext.console = {
        log: function (t) {
            WScript.Echo("    " + t);
        },
        info: function (t) {
            WScript.Echo("[?] " + t);
        },
        warn: function (t) {
            WScript.Echo("/!\\ " + t);
        },
        error: function (t) {
            WScript.Echo("(X) " + t);
        }
    };

/*#ifndef(UMD)*/

    _gpfMsFSO = new ActiveXObject("Scripting.FileSystemObject");

    _gpfSyncReadForBoot = function (srcFileName) {
        var srcFile = _gpfMsFSO.OpenTextFile(srcFileName),
            srcContent = srcFile.ReadAll();
        srcFile.Close();
        return srcContent;
    };

/*#endif*/

    /*eslint-enable new-cap*/
} else if ("undefined" !== typeof print && "undefined" !== typeof java) {
    _gpfHost = _GPF_HOST_RHINO;
    _gpfDosPath = false;
    _gpfMainContext = function () {
        return this;
    }.call(null);
    _gpfExit = function (code) {
        java.lang.System.exit(code);
    };

    // Define console APIs
    _gpfMainContext.console = {
        log: function (t) {
            print("    " + t);
        },
        info: function (t) {
            print("[?] " + t);
        },
        warn: function (t) {
            print("/!\\ " + t);
        },
        error: function (t) {
            print("(X) " + t);
        }
    };

/*#ifndef(UMD)*/

    _gpfSyncReadForBoot = readFile;

/*#endif*/

// PhantomJS
} else if ("undefined" !== typeof phantom && phantom.version) {
    _gpfHost = _GPF_HOST_PHANTOMJS;
    _gpfDosPath = require("fs").separator === "\\";
    _gpfMainContext = window;
    _gpfInNode = true;
    _gpfInBrowser = true;
    _gpfExit = phantom.exit;

/*#ifndef(UMD)*/

    _gpfNodeFs =  require("fs");

    _gpfSyncReadForBoot = function (srcFileName) {
        return _gpfNodeFs.read(srcFileName);
    };

/*#endif*/

// Nodejs
} else if ("undefined" !== typeof module && module.exports) {
    _gpfHost = _GPF_HOST_NODEJS;
    _gpfNodePath = require("path");
    _gpfDosPath = _gpfNodePath.sep === "\\";
    _gpfMainContext = global;
    _gpfInNode = true;
    _gpfExit = process.exit;

/*#ifndef(UMD)*/

    /*eslint-disable no-sync*/ // Simpler this way

    _gpfNodeFs =  require("fs");

    _gpfSyncReadForBoot = function (srcFileName) {
        return _gpfNodeFs.readFileSync(srcFileName).toString();
    };

/*#endif*/

// Browser
} else if ("undefined" !== typeof window) {
    _gpfHost = _GPF_HOST_BROWSER;
    _gpfMainContext = window;
    _gpfInBrowser = true;
    _gpfExit = _gpfEmptyFunc;
    _gpfWebWindow = window;
    _gpfWebDocument = document;
    _gpfWebHead = _gpfWebDocument.getElementsByTagName("head")[0] || _gpfWebDocument.documentElement;

/*#ifndef(UMD)*/

    _gpfSyncReadForBoot = function (srcFileName) {
        var xhr = new XMLHttpRequest();
        xhr.open("GET", srcFileName, false);
        xhr.send();
        return xhr.responseText;
    };

/*#endif*/

}

/*#ifndef(UMD)*/

_gpfMainContext.gpf = {
    internals: {} // To support testable internals
};

/*#endif*/

// Install host specifics (if any)
/* istanbul ignore else */ // Because tested with NodeJS
if (_gpfInNode) {
    gpf.node = {};
}
// Some web-related tools will be configured even if not in a browser, declare the namespace now
gpf.web = {};

// Returns the current version
gpf.version = function () {
    return _gpfVersion;
};

/**
 * Returns a string identifying the detected host
 *
 * @return {String}
 * - gpf.HOST_WSCRIPT for cscript and wscript
 * - gpf.HOST_NODEJS for nodejs
 * - gpf.HOST_PHANTOMJS for phantomjs
 * - gpf.HOST_BROWSER for any browser
 * - gpf.HOST_UNKNOWN if not detected
 */
gpf.host = function () {
    return _gpfHost;
};

/*#ifndef(UMD)*/

var
    // Set to true once all sources of GPF are loaded
    _gpfLoaded = false,

    /**
     * List of functions to call on load
     *
     * @type {Function[]}
     */
    _gpfLoadCallbacks = [];

// Callback used once all sources are loaded, invoke the callbacks
function _gpfFinishLoading () {
    _gpfLoaded = true;
    while (_gpfLoadCallbacks.length) {
        /* istanbul ignore next */ // Not used within NodeJS (mostly browser only)
        _gpfLoadCallbacks.shift()();
    }
}

/* istanbul ignore next */ // Not used within NodeJS (mostly browser only)
/**
 * Test if GPF is loaded
 *
 * @param {Function} [callback=undefined] callback This callback is invoked when GPF is loaded (may be immediately if
 * already loaded)
 * @return {Boolean} True if GPF is fully loaded, false otherwise
 */
gpf.loaded = function (callback) {
    if (callback) {
        if (_gpfLoaded) {
            callback();
        } else {
            _gpfLoadCallbacks.push(callback);
        }
    }
    return _gpfLoaded;
};

/* istanbul ignore if */ // Because tested in DEBUG
if (!gpf.loaded) {

/*#else*/

    gpf.loaded = function (callback) {
        if (callback) {
            callback();
        }
        return true;
    };

/*#endif*/

/*#ifndef(UMD)*/

}

/*#endif*/

/*#ifndef(UMD)*/

/**
 * Loading sources occurs here for the non UMD version.
 * UMD versions (debug / release) will have everything concatenated.
 */

/* istanbul ignore next */ // Handled the right way with NodeJS
// gpfSourcesPath - if defined - gives the relative path to sources
if ("undefined" === typeof gpfSourcesPath) {
    _gpfMainContext.gpfSourcesPath = "";
} else {
    var pathSep;
    if (_gpfDosPath) {
        pathSep = "\\";
    } else {
        pathSep = "/";
    }
    if (gpfSourcesPath.charAt(gpfSourcesPath.length - 1) !== pathSep) {
        gpfSourcesPath += pathSep;
    }
}

var _gpfSourcesContent = _gpfSyncReadForBoot(gpfSourcesPath + "sources.js");
/*jslint evil: true*/
eval(_gpfSourcesContent); //eslint-disable-line no-eval
/*jslint evil: false*/
var _gpfSources = gpf.sources(),
    _gpfAllContent = [],
    _gpfSourceIdx = 0,
    _gpfSourceModule;
for (_gpfSourceIdx = 0; _gpfSourceIdx < _gpfSources.length; ++_gpfSourceIdx) {
    _gpfSourceModule = _gpfSources[_gpfSourceIdx];
    if (!_gpfSourceModule) {
        break;
    }
    _gpfAllContent.push(_gpfSyncReadForBoot(gpfSourcesPath + _gpfSourceModule + ".js"));
}
/*jslint evil: true*/
eval(_gpfAllContent.join("\r\n")); //eslint-disable-line no-eval
/*jslint evil: false*/
_gpfFinishLoading();

/*#endif*/
<|endoftext|>


<commit_before>(function (window, document, Card, angular, undefined) {
  'use strict';
  angular
    .module('gavruk.card', [])

  .controller('CardCtrl', function ($scope) {})

  .directive('card', function ($compile) {
    return {
      restrict: 'A',
      scope: {
        cardContainer: '@', // required
        width: '@',
        placeholders: '=',
        options: '=',
        messages: '=',
      },
      controller: 'CardCtrl',
      link: function (scope, element, attributes, cardCtrl) {
        var defaultPlaceholders = {
          number: '•••• •••• •••• ••••',
          name: 'Full Name',
          expiry: '••/••',
          cvc: '•••'
        };
        var defaultMessages = {
          validDate: 'valid\nthru',
          monthYear: 'month/year',
        };
        var defaultOptions = {
          debug: false,
          formatting: true
        };

        var placeholders = angular.extend(defaultPlaceholders, scope.placeholders);
        var messages = angular.extend(defaultMessages, scope.messages);
        var options = angular.extend(defaultOptions, scope.options);

        var opts = {
          form: '[name=' + attributes.name + ']',

          // a selector or jQuery object for the container
          // where you want the card to appear
          container: scope.cardContainer, // *required*

          formSelectors: {},

          width: scope.width || 350,

          // Strings for translation - optional
          messages: {
            validDate: messages.validDate,
            monthYear: messages.monthYear
          },

          // Default placeholders for rendered fields - options
          placeholders: {
            number: placeholders.number,
            name: placeholders.name,
            expiry: placeholders.expiry,
            cvc: placeholders.cvc
          },

          formatting: options.formatting, // optional - default true
          debug: options.debug // if true, will log helpful messages for setting up Card
        };

        if (cardCtrl.numberInput && cardCtrl.numberInput.length > 0) {
          opts.formSelectors.numberInput = 'input[name="' + cardCtrl.numberInput[0].name + '"]';
        }
        if (cardCtrl.expiryInput && cardCtrl.expiryInput.length > 0) {
          opts.formSelectors.expiryInput = 'input[name="' + cardCtrl.expiryInput[0].name + '"]';
        }
        if (cardCtrl.cvcInput && cardCtrl.cvcInput.length > 0) {
          opts.formSelectors.cvcInput = 'input[name="' + cardCtrl.cvcInput[0].name + '"]';
        }
        if (cardCtrl.nameInput && cardCtrl.nameInput.length > 0) {
          opts.formSelectors.nameInput = 'input[name="' + cardCtrl.nameInput[0].name + '"]';
        }

        new Card(opts);
      }
    };
  })

  .directive('cardNumber', function ($compile) {
    return {
      restrict: 'A',
      scope: {
        ngModel: '='
      },
      require: [
        '^card',
        'ngModel'
      ],
      link: function (scope, element, attributes, ctrls) {
        var cardCtrl = ctrls[0];
        cardCtrl.numberInput = element;
        scope.$watch('ngModel', function (newVal, oldVal) {
          if (!oldVal && !newVal) {
            return;
          }
          if (oldVal === newVal && !newVal) {
            return;
          }

          var evt = document.createEvent('HTMLEvents');
          evt.initEvent('keyup', false, true);
          element[0].dispatchEvent(evt);
        });
      }
    };
  })

  .directive('cardName', function ($compile) {
    return {
      restrict: 'A',
      scope: {
        ngModel: '='
      },
      require: [
        '^card',
        'ngModel'
      ],
      link: function (scope, element, attributes, ctrls) {
        var cardCtrl = ctrls[0];
        cardCtrl.nameInput = element;
        scope.$watch('ngModel', function (newVal, oldVal) {
          if (!oldVal && !newVal) {
            return;
          }
          if (oldVal === newVal && !newVal) {
            return;
          }

          var evt = document.createEvent('HTMLEvents');
          evt.initEvent('keyup', false, true);
          element[0].dispatchEvent(evt);
        });
      }
    };
  })

  .directive('cardExpiry', function ($compile) {
    return {
      restrict: 'A',
      scope: {
        ngModel: '='
      },
      require: [
        '^card',
        'ngModel'
      ],
      link: function (scope, element, attributes, ctrls) {
        var cardCtrl = ctrls[0];
        cardCtrl.expiryInput = element;
        scope.$watch('ngModel', function (newVal, oldVal) {
          if (!oldVal && !newVal) {
            return;
          }
          if (oldVal === newVal && !newVal) {
            return;
          }

          var evt = document.createEvent('HTMLEvents');
          evt.initEvent('keyup', false, true);
          element[0].dispatchEvent(evt);
        });
      }
    };
  })

  .directive('cardCvc', function ($compile) {
    return {
      restrict: 'A',
      scope: {
        ngModel: '='
      },
      require: [
        '^card',
        'ngModel'
      ],
      link: function (scope, element, attributes, ctrls) {
        var cardCtrl = ctrls[0];
        cardCtrl.cvcInput = element;
        scope.$watch('ngModel', function (newVal, oldVal) {
          if (!oldVal && !newVal) {
            return;
          }
          if (oldVal === newVal && !newVal) {
            return;
          }

          var evt = document.createEvent('HTMLEvents');
          evt.initEvent('keyup', false, true);
          element[0].dispatchEvent(evt);
        });
      }
    };
  });

})(window, window.document, window.Card, window.angular);
<commit_msg>Fix #8<commit_after>(function (window, document, Card, angular, undefined) {
  'use strict';
  angular
    .module('gavruk.card', [])

  .controller('CardCtrl', ['$scope', function ($scope) {
  }])

  .directive('card', ['$compile', function ($compile) {
    return {
      restrict: 'A',
      scope: {
        cardContainer: '@', // required
        width: '@',
        placeholders: '=',
        options: '=',
        messages: '=',
      },
      controller: 'CardCtrl',
      link: function (scope, element, attributes, cardCtrl) {
        var defaultPlaceholders = {
          number: '•••• •••• •••• ••••',
          name: 'Full Name',
          expiry: '••/••',
          cvc: '•••'
        };
        var defaultMessages = {
          validDate: 'valid\nthru',
          monthYear: 'month/year',
        };
        var defaultOptions = {
          debug: false,
          formatting: true
        };

        var placeholders = angular.extend(defaultPlaceholders, scope.placeholders);
        var messages = angular.extend(defaultMessages, scope.messages);
        var options = angular.extend(defaultOptions, scope.options);

        var opts = {
          form: '[name=' + attributes.name + ']',

          // a selector or jQuery object for the container
          // where you want the card to appear
          container: scope.cardContainer, // *required*

          formSelectors: {},

          width: scope.width || 350,

          // Strings for translation - optional
          messages: {
            validDate: messages.validDate,
            monthYear: messages.monthYear
          },

          // Default placeholders for rendered fields - options
          placeholders: {
            number: placeholders.number,
            name: placeholders.name,
            expiry: placeholders.expiry,
            cvc: placeholders.cvc
          },

          formatting: options.formatting, // optional - default true
          debug: options.debug // if true, will log helpful messages for setting up Card
        };

        if (cardCtrl.numberInput && cardCtrl.numberInput.length > 0) {
          opts.formSelectors.numberInput = 'input[name="' + cardCtrl.numberInput[0].name + '"]';
        }
        if (cardCtrl.expiryInput && cardCtrl.expiryInput.length > 0) {
          opts.formSelectors.expiryInput = 'input[name="' + cardCtrl.expiryInput[0].name + '"]';
        }
        if (cardCtrl.cvcInput && cardCtrl.cvcInput.length > 0) {
          opts.formSelectors.cvcInput = 'input[name="' + cardCtrl.cvcInput[0].name + '"]';
        }
        if (cardCtrl.nameInput && cardCtrl.nameInput.length > 0) {
          opts.formSelectors.nameInput = 'input[name="' + cardCtrl.nameInput[0].name + '"]';
        }

        new Card(opts);
      }
    };
  }])

  .directive('cardNumber', ['$compile', function ($compile) {
    return {
      restrict: 'A',
      scope: {
        ngModel: '='
      },
      require: [
        '^card',
        'ngModel'
      ],
      link: function (scope, element, attributes, ctrls) {
        var cardCtrl = ctrls[0];
        cardCtrl.numberInput = element;
        scope.$watch('ngModel', function (newVal, oldVal) {
          if (!oldVal && !newVal) {
            return;
          }
          if (oldVal === newVal && !newVal) {
            return;
          }

          var evt = document.createEvent('HTMLEvents');
          evt.initEvent('keyup', false, true);
          element[0].dispatchEvent(evt);
        });
      }
    };
  }])

  .directive('cardName', ['$compile', function ($compile) {
    return {
      restrict: 'A',
      scope: {
        ngModel: '='
      },
      require: [
        '^card',
        'ngModel'
      ],
      link: function (scope, element, attributes, ctrls) {
        var cardCtrl = ctrls[0];
        cardCtrl.nameInput = element;
        scope.$watch('ngModel', function (newVal, oldVal) {
          if (!oldVal && !newVal) {
            return;
          }
          if (oldVal === newVal && !newVal) {
            return;
          }

          var evt = document.createEvent('HTMLEvents');
          evt.initEvent('keyup', false, true);
          element[0].dispatchEvent(evt);
        });
      }
    };
  }])

  .directive('cardExpiry', ['$compile', function ($compile) {
    return {
      restrict: 'A',
      scope: {
        ngModel: '='
      },
      require: [
        '^card',
        'ngModel'
      ],
      link: function (scope, element, attributes, ctrls) {
        var cardCtrl = ctrls[0];
        cardCtrl.expiryInput = element;
        scope.$watch('ngModel', function (newVal, oldVal) {
          if (!oldVal && !newVal) {
            return;
          }
          if (oldVal === newVal && !newVal) {
            return;
          }

          var evt = document.createEvent('HTMLEvents');
          evt.initEvent('keyup', false, true);
          element[0].dispatchEvent(evt);
        });
      }
    };
  }])

  .directive('cardCvc', ['$compile', function ($compile) {
    return {
      restrict: 'A',
      scope: {
        ngModel: '='
      },
      require: [
        '^card',
        'ngModel'
      ],
      link: function (scope, element, attributes, ctrls) {
        var cardCtrl = ctrls[0];
        cardCtrl.cvcInput = element;
        scope.$watch('ngModel', function (newVal, oldVal) {
          if (!oldVal && !newVal) {
            return;
          }
          if (oldVal === newVal && !newVal) {
            return;
          }

          var evt = document.createEvent('HTMLEvents');
          evt.initEvent('keyup', false, true);
          element[0].dispatchEvent(evt);
        });
      }
    };
  }]);

})(window, window.document, window.Card, window.angular);
<|endoftext|>


<commit_before>"use strict";
(function(window) {
    function Moon(opts) {
        var _el = opts.el;
        var _data = opts.data;
        var _methods = opts.methods;
        var directives = {};
        var self = this;
        this.$el = document.querySelector(_el);
        this.components = opts.components;
        this.dom = {type: this.$el.nodeName, children: [], node: this.$el};

        // Change state when $data is changed
        Object.defineProperty(this, '$data', {
            get: function() {
                return _data;
            },
            set: function(value) {
                _data = value;
                this.build(this.dom.children);
            }
        });

        /**
        * Converts attributes into key-value pairs
        * @param {Node} node
        * @return {Object} Key-Value pairs of Attributes
        */
        var extractAttrs = function(node) {
          var attrs = {};
          if(!node.attributes) return attrs;
          var rawAttrs = node.attributes;
          for(var i = 0; i < rawAttrs.length; i++) {
            attrs[rawAttrs[i].name] = rawAttrs[i].value
          }

          return attrs;
        }

        /**
        * Compiles a template with given data
        * @param {String} template
        * @param {Object} data
        * @return {String} Template with data rendered
        */
        var compileTemplate = function(template, data) {
          var code = template,
              re = /{{([.]?\w+)}}/gi;
          code.replace(re, function(match, p) {
            code = code.replace(match, "` + data." + p + " + `");
          });
          var compile = new Function("data", "var out = `" + code + "`; return out");
          return compile(data);
        }

        /**
        * Creates an object to be used in a Virtual DOM
        * @param {String} type
        * @param {Array} children
        * @param {String} val
        * @param {Object} props
        * @param {Node} node
        * @return {Object} Object usable in Virtual DOM
        */
        this.createElement = function(type, children, val, props, node) {
          return {type: type, children: children, val: val, props: props, node: node};
        }

        /**
        * Create Elements Recursively For all Children
        * @param {Array} children
        * @return {Array} Array of elements usable in Virtual DOM
        */
        this.recursiveChildren = function(children) {
          var recursiveChildrenArr = [];
          for(var i = 0; i < children.length; i++) {
            var child = children[i];
            recursiveChildrenArr.push(this.createElement(child.nodeName, this.recursiveChildren(child.childNodes), child.textContent, extractAttrs(child), child));
          }
          return recursiveChildrenArr;
        }

        /**
        * Creates Virtual DOM
        * @param {Node} node
        */
        this.createVirtualDOM = function(node) {
          var vdom = this.createElement(node.nodeName, this.recursiveChildren(node.childNodes), node.textContent, extractAttrs(node), node);
          this.dom = vdom;
        }

        /**
        * Turns Custom Components into their Corresponding Templates
        */
        this.componentsToHTML = function() {
          for(var component in this.components) {
            var componentsFound = document.getElementsByTagName(component);
            for(var i = 0; i < componentsFound.length; i++) {
              var componentFound = componentsFound[i];
              var componentProps = extractAttrs(componentFound);
              var componentDummy = document.createElement('div');
              componentDummy.innerHTML = this.components[component].template;
              componentDummy = componentDummy.firstChild;

              for(var attr in componentProps) {
                componentDummy.setAttribute(attr, componentProps[attr]);
              }

              componentDummy.setAttribute("m-component");

              componentFound.outerHTML = componentDummy.outerHTML;
            }
          }
        }

        /**
        * Sets Value in Data
        * @param {String} key
        * @param {String} val
        */
        this.set = function(key, val) {
          this.$data[key] = val;
          this.build(this.dom.children);
        }

        /**
        * Gets Value in Data
        * @param {String} key
        * @return {String} Value of key in data
        */
        this.get = function(key) {
          return this.$data[key];
        }

        /**
        * Makes an AJAX Request
        * @param {String} method
        * @param {String} url
        * @param {Object} params
        * @param {Function} cb
        */
        this.ajax = function(method, url, params, cb) {
          var xmlHttp = new XMLHttpRequest();
          method = method.toUpperCase();
          if(typeof params === "function") {
            cb = params;
          }
          var urlParams = "?";
          if(method === "POST") {
            http.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
            for(var param in params) {
              urlParams += param + "=" + params[param] + "&";
            }
          }
          xmlHttp.onreadystatechange = function() {
          if (xmlHttp.readyState == 4 && xmlHttp.status == 200)
            cb(JSON.parse(xmlHttp.responseText));
          }
          xmlHttp.open(method, url, true);
          xmlHttp.send(method === "POST" ? urlParams : null);
        }

        /**
        * Calls a method
        * @param {String} method
        */
        this.method = function(method) {
          _methods[method]();
        }

        // Directive Initialization
        this.directive = function(name, action) {
          directives["m-" + name] = action;
        }

        // Default Directives
        directives["m-if"] = function(el, val, vdom) {
          var evaluated = new Function("return " + val);
          if(!evaluated()) {
            el.textContent = "";
          } else {
            el.textContent = compileTemplate(vdom.val, self.$data);
          }
        }

        directives["m-on"] = function(el, val, vdom) {
          var splitVal = val.split(":");
          var eventToCall = splitVal[0];
          var methodToCall = splitVal[1];
          el.addEventListener(eventToCall, function() {
            self.method(methodToCall);
          });
          el.removeAttribute("m-on");
          delete vdom.props["m-on"];
        }

        /**
        * Builds the DOM With Data
        * @param {Array} children
        */
        this.build = function(children) {
          for(var i = 0; i < children.length; i++) {
            var el = children[i];

            if(el.type === "#text") {
              el.node.textContent = compileTemplate(el.val, this.$data);
            } else if(el.props) {
              for(var prop in el.props) {
                var propVal = el.props[prop];
                var compiledProperty = compileTemplate(propVal, this.$data);
                var directive = directives[prop];
                if(directive) {
                  directive(el.node, compiledProperty, el);
                }

                el.node.setAttribute(prop, compiledProperty);
              }
            }

            this.build(el.children);
          }
        }

        // Initialize 🎉
        this.componentsToHTML();
        this.createVirtualDOM(this.$el);
        this.build(this.dom.children);
    }

    window.Moon = Moon;
    window.$ = function(el) {
      el = document.querySelectorAll(el);
      return el.length === 1 ? el[0] : el;
    }

})(window);
<commit_msg>set it to nothing<commit_after>"use strict";
(function(window) {
    function Moon(opts) {
        var _el = opts.el;
        var _data = opts.data;
        var _methods = opts.methods;
        var directives = {};
        var self = this;
        this.$el = document.querySelector(_el);
        this.components = opts.components;
        this.dom = {type: this.$el.nodeName, children: [], node: this.$el};

        // Change state when $data is changed
        Object.defineProperty(this, '$data', {
            get: function() {
                return _data;
            },
            set: function(value) {
                _data = value;
                this.build(this.dom.children);
            }
        });

        /**
        * Converts attributes into key-value pairs
        * @param {Node} node
        * @return {Object} Key-Value pairs of Attributes
        */
        var extractAttrs = function(node) {
          var attrs = {};
          if(!node.attributes) return attrs;
          var rawAttrs = node.attributes;
          for(var i = 0; i < rawAttrs.length; i++) {
            attrs[rawAttrs[i].name] = rawAttrs[i].value
          }

          return attrs;
        }

        /**
        * Compiles a template with given data
        * @param {String} template
        * @param {Object} data
        * @return {String} Template with data rendered
        */
        var compileTemplate = function(template, data) {
          var code = template,
              re = /{{([.]?\w+)}}/gi;
          code.replace(re, function(match, p) {
            code = code.replace(match, "` + data." + p + " + `");
          });
          var compile = new Function("data", "var out = `" + code + "`; return out");
          return compile(data);
        }

        /**
        * Creates an object to be used in a Virtual DOM
        * @param {String} type
        * @param {Array} children
        * @param {String} val
        * @param {Object} props
        * @param {Node} node
        * @return {Object} Object usable in Virtual DOM
        */
        this.createElement = function(type, children, val, props, node) {
          return {type: type, children: children, val: val, props: props, node: node};
        }

        /**
        * Create Elements Recursively For all Children
        * @param {Array} children
        * @return {Array} Array of elements usable in Virtual DOM
        */
        this.recursiveChildren = function(children) {
          var recursiveChildrenArr = [];
          for(var i = 0; i < children.length; i++) {
            var child = children[i];
            recursiveChildrenArr.push(this.createElement(child.nodeName, this.recursiveChildren(child.childNodes), child.textContent, extractAttrs(child), child));
          }
          return recursiveChildrenArr;
        }

        /**
        * Creates Virtual DOM
        * @param {Node} node
        */
        this.createVirtualDOM = function(node) {
          var vdom = this.createElement(node.nodeName, this.recursiveChildren(node.childNodes), node.textContent, extractAttrs(node), node);
          this.dom = vdom;
        }

        /**
        * Turns Custom Components into their Corresponding Templates
        */
        this.componentsToHTML = function() {
          for(var component in this.components) {
            var componentsFound = document.getElementsByTagName(component);
            for(var i = 0; i < componentsFound.length; i++) {
              var componentFound = componentsFound[i];
              var componentProps = extractAttrs(componentFound);
              var componentDummy = document.createElement('div');
              componentDummy.innerHTML = this.components[component].template;
              componentDummy = componentDummy.firstChild;

              for(var attr in componentProps) {
                componentDummy.setAttribute(attr, componentProps[attr]);
              }

              componentDummy.setAttribute("m-component", "");

              componentFound.outerHTML = componentDummy.outerHTML;
            }
          }
        }

        /**
        * Sets Value in Data
        * @param {String} key
        * @param {String} val
        */
        this.set = function(key, val) {
          this.$data[key] = val;
          this.build(this.dom.children);
        }

        /**
        * Gets Value in Data
        * @param {String} key
        * @return {String} Value of key in data
        */
        this.get = function(key) {
          return this.$data[key];
        }

        /**
        * Makes an AJAX Request
        * @param {String} method
        * @param {String} url
        * @param {Object} params
        * @param {Function} cb
        */
        this.ajax = function(method, url, params, cb) {
          var xmlHttp = new XMLHttpRequest();
          method = method.toUpperCase();
          if(typeof params === "function") {
            cb = params;
          }
          var urlParams = "?";
          if(method === "POST") {
            http.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
            for(var param in params) {
              urlParams += param + "=" + params[param] + "&";
            }
          }
          xmlHttp.onreadystatechange = function() {
          if (xmlHttp.readyState == 4 && xmlHttp.status == 200)
            cb(JSON.parse(xmlHttp.responseText));
          }
          xmlHttp.open(method, url, true);
          xmlHttp.send(method === "POST" ? urlParams : null);
        }

        /**
        * Calls a method
        * @param {String} method
        */
        this.method = function(method) {
          _methods[method]();
        }

        // Directive Initialization
        this.directive = function(name, action) {
          directives["m-" + name] = action;
        }

        // Default Directives
        directives["m-if"] = function(el, val, vdom) {
          var evaluated = new Function("return " + val);
          if(!evaluated()) {
            el.textContent = "";
          } else {
            el.textContent = compileTemplate(vdom.val, self.$data);
          }
        }

        directives["m-on"] = function(el, val, vdom) {
          var splitVal = val.split(":");
          var eventToCall = splitVal[0];
          var methodToCall = splitVal[1];
          el.addEventListener(eventToCall, function() {
            self.method(methodToCall);
          });
          el.removeAttribute("m-on");
          delete vdom.props["m-on"];
        }

        /**
        * Builds the DOM With Data
        * @param {Array} children
        */
        this.build = function(children) {
          for(var i = 0; i < children.length; i++) {
            var el = children[i];

            if(el.type === "#text") {
              el.node.textContent = compileTemplate(el.val, this.$data);
            } else if(el.props) {
              for(var prop in el.props) {
                var propVal = el.props[prop];
                var compiledProperty = compileTemplate(propVal, this.$data);
                var directive = directives[prop];
                if(directive) {
                  directive(el.node, compiledProperty, el);
                }

                el.node.setAttribute(prop, compiledProperty);
              }
            }

            this.build(el.children);
          }
        }

        // Initialize 🎉
        this.componentsToHTML();
        this.createVirtualDOM(this.$el);
        this.build(this.dom.children);
    }

    window.Moon = Moon;
    window.$ = function(el) {
      el = document.querySelectorAll(el);
      return el.length === 1 ? el[0] : el;
    }

})(window);
<|endoftext|>


<commit_before>define(
	[
		"./htmlCanvas",
		"./widget",
		"./widget-extensions",
		"./router",
		"./events"
	],

	function(htmlCanvas, widget, ext, router, events) {
		return {
			htmlCanvas : htmlCanvas,
			widget : widget,
			ext : ext,
			router : router,
			events : events
		};
	}
);<commit_msg>Add newline at end of file<commit_after>define(
	[
		"./htmlCanvas",
		"./widget",
		"./widget-extensions",
		"./router",
		"./events"
	],

	function(htmlCanvas, widget, ext, router, events) {
		return {
			htmlCanvas : htmlCanvas,
			widget : widget,
			ext : ext,
			router : router,
			events : events
		};
	}
);
<|endoftext|>