<commit_before>var fs = require('fs');
var crypto = require('crypto');
var path = require('path');
var mkdirp = require('mkdirp');
var request = require('request');
var zlib = require('zlib');
var changes = require('concurrent-couch-follower');
var JSONStream = require('JSONStream');
var es = require('event-stream');
var etag = require('nginx-etag');

var config = require('./configLoader');

var logger = config.logger;

var api = {};

var httpCentralUrl = config.centralURL.replace('https', 'http');
var httpsCentralUrl = httpCentralUrl.replace('http', 'https');
var sequenceFile = path.join(config.attachmentPath, '.sequence');

function verifyShasum(tmpFilePath, tarball, filePath) {
  var hash = crypto.createHash('sha1');
  fs.createReadStream(tmpFilePath).on('data', function(data) {
    hash.update(data);
  })
    .on('end', function() {
      var shasum = hash.digest('hex');
      var targetShasum;
      request({
        // We request ourselves to benefit from JSON caching!
        baseUrl: 'http://localhost:' + config.port,
        url: tarball.name
      })
      // Extract shasum of the correct version
        .pipe(JSONStream.parse(['versions', tarball.version, 'dist', 'shasum']))
        .pipe(es.mapSync(function(data) {
          targetShasum = data;
          if (targetShasum === shasum) {
            logger.trace('Correct shasum %s for file %s.', shasum, tmpFilePath);
            mkdirp(path.dirname(filePath), function() {
              fs.rename(tmpFilePath, filePath);
            });
          } else {
            logger.error('Incorrect shasum %s for file %s (expected: %s): we delete the file.', shasum, tmpFilePath, targetShasum);
            fs.unlink(tmpFilePath);
          }
        }))
        .on('end', function() {
          if (targetShasum === undefined) {
            logger.error('Unavailable shasum for version %s of module %s: we delete the file.', tarball.version, tarball.name);
            fs.unlink(tmpFilePath);
          }
        });
    });
}

function verifyJson(tmpFilePath, filePath) {
  fs.readFile(tmpFilePath, 'utf8', function(err, jsonData) {
    var isJSON = true;
    try {
      var json = JSON.parse(jsonData);
    } catch (e) {
      isJSON = false;
      logger.error('File %s is not a valid JSON: we delete the JSON files.', tmpFilePath, e);
    }
    if (!isJSON || !json.name || !json.versions || !json.time) {
      if (isJSON) {
        logger.error('File is a valid JSON but it lacks one property among "name", "versions" and "time".');
      }
      fs.unlinkSync(tmpFilePath);
      fs.unlinkSync(filePath);
    } else {
      logger.trace('File %s is a valid JSON.', tmpFilePath);
      var latest = (json['dist-tags']) ? json['dist-tags'].latest : '';
      logger.debug('Saving new version of %s: %s', json.name, latest);
      mkdirp(path.dirname(filePath), function() {
        fs.rename(tmpFilePath, filePath);
      });
    }
  })
}

/**
 * Serve attachment from file system if present, else serve it from central registry (and save it on file system)
 * @param req request
 * @param resp response
 * @param {Tarball} tarball - tarball
 */
api.manageAttachment = function(req, resp, tarball) {
  var fileName = path.basename(req.url);
  var tmpFilePath = path.join(config.attachmentPath, fileName + '.tmp');
  var filePath = path.join(config.attachmentPath, req.url);
  fs.exists(filePath, function(exists) {
    if (exists) {
      logger.debug('%s: serving attachment from file system: %s', req.url, filePath);
      fs.createReadStream(filePath).pipe(resp);
    } else {
      var downloadURL = config.centralURL + req.url;
      logger.debug('%s: downloading, saving and serving attachment from central registry: %s', req.url, downloadURL);
      var couchDBStream = request(downloadURL);
      req.pipe(couchDBStream);
      couchDBStream.pipe(resp);
      var tmpFileStream = fs.createWriteStream(tmpFilePath);
      couchDBStream.pipe(tmpFileStream);

      tmpFileStream.on('finish', function() {
        logger.trace('File %s is completely written', tmpFilePath);
        verifyShasum(tmpFilePath, tarball, filePath);
      });
    }
  });
};

function unzipIfRequired(response) {
  var contentEncoding = response.headers['content-encoding'];
  if (contentEncoding === 'gzip') {
    // Response is a gzip stream, we gunzip it in order to be able to replace content
    response = response.pipe(zlib.createGunzip());
  } else if (contentEncoding === 'deflate') {
    // Response is a deflate stream, we inflate it in order to be able to replace content
    response = response.pipe(zlib.createInflate());
  }
  return response;
}

function proxyToCentralRepositoryAndReplaceUrls(req, resp) {
  var centralCouchDBUrl = config.centralURL;
  logger.debug('%s: forwarding to repository (%s) and responding with attachment URL replacement', req.url, centralCouchDBUrl);
  var centralRegistry = {
    baseUrl: centralCouchDBUrl,
    url: req.url
  };
  req.pipe(request(centralRegistry)).on('response', function(response) {
    response = unzipIfRequired(response);
    response
      .pipe(es.replace(httpsCentralUrl, config.proxyURL))
      .pipe(es.replace(httpCentralUrl, config.proxyURL))
      .pipe(resp);
  });
}
/**
 * Serve JSON of a module from local (or central) registry with attachment URL replacement
 * @param req request
 * @param resp response
 * @param {Module} module - module
 */
api.manageModule = function(req, resp, module) {
  // TODO: We do NOT handle requests with version for now
  if (module.version) {
    proxyToCentralRepositoryAndReplaceUrls(req, resp);
  } else {
    var tmpFilePath = path.join(config.attachmentPath, module.name + '.json.tmp');
    var filePath = path.join(config.attachmentPath, module.name + '.json');
    fs.exists(filePath, function(exists) {
      if (exists) {
        etag.fromFile(filePath, function(err, jsonEtag) {
          var ifNoneMatch = req.headers['if-none-match'];
          if (ifNoneMatch && ifNoneMatch === jsonEtag) {
            logger.debug('%s: client cache up to date. Replying "Not Modified"', req.url);
            resp.writeHead(304);
            resp.end();
          } else {
            logger.debug('%s: serving JSON from file system: %s', req.url, filePath);
            resp.setHeader('etag', jsonEtag);
            fs.createReadStream(filePath)
              .pipe(es.replace(httpsCentralUrl, config.proxyURL))
              .pipe(es.replace(httpCentralUrl, config.proxyURL))
              .pipe(resp);
          }
        });
      } else {
        var downloadURL = config.centralURL + req.url;
        logger.debug('%s: downloading, saving and serving JSON from central registry: %s', req.url, downloadURL);
        req.pipe(request(downloadURL))
          .on('response', function(response) {
            response = unzipIfRequired(response);
            response
              .pipe(es.replace(httpsCentralUrl, config.proxyURL))
              .pipe(es.replace(httpCentralUrl, config.proxyURL))
              .pipe(resp);
            var tmpFileStream = fs.createWriteStream(tmpFilePath);
            response.pipe(tmpFileStream);
            tmpFileStream.on('finish', function() {
              logger.trace('File %s is completely written', tmpFilePath);
              verifyJson(tmpFilePath, filePath);
            })
          });
      }
    });
  }
};

/**
 * Proxy to central repository
 * @param req request
 * @param resp response
 */
api.proxyToCentralRepository = function(req, resp) {
  var centralCouchDBUrl = config.centralURL;
  logger.debug('%s: forwarding to central repository (%s)', req.url, centralCouchDBUrl);
  var centralRegistry = {
    baseUrl: centralCouchDBUrl,
    url: req.url
  };
  req.pipe(request(centralRegistry)).pipe(resp);
};

api.initChangesSync = function() {
  mkdirp.sync(path.dirname(sequenceFile));

  var stream = changes(function(data, done) {
    // Replace "/" by "%2f" for scoped modules
    var moduleName = data.id.replace('/', '%2f');
    var fileName = moduleName + '.json';
    var filePath = path.join(config.attachmentPath, fileName);

    fs.exists(filePath, function(exists) {
      if (exists) {
        logger.debug('Updating %s module (seq: %s)', data.id, data.seq);
        // We can't use data.doc as it could contain invalid semver (e.g.: grunt-0.4.0a). To prevent this problem, we then download the JSON from central registry which retains only valid versions.
        // As change detection and json download are from 2 different server, we add a delay to prevent downloading a not up to date json.
        setTimeout(function() {
          // Delete deprecated file
          fs.unlinkSync(filePath);
          // Request the module from ourselves to trigger json save
          request(
            {
              baseUrl: 'http://localhost:' + config.port,
              url: moduleName
            },
            done
          );
        }, config.delay);
      } else {
        logger.trace('New %s module (seq: %s), but we don\'t use it', data.id, data.seq);
        done();
      }
    });
  }, {
    db: config.syncURL,
    now: true,
    sequence: sequenceFile,
    include_docs: false,
    concurrency: 1
  });

  var seq = stream.sequence();
  if (seq === 0) {
    logger.debug('Starting sync from latest sequence number on %s', config.syncURL);
  } else {
    logger.debug('Restarting sync from %s since sequence number %s', config.syncURL, seq);
  }
};

module.exports = api;
<commit_msg>Prevent unlink errors if file doesn't exist<commit_after>var fs = require('fs');
var crypto = require('crypto');
var path = require('path');
var mkdirp = require('mkdirp');
var request = require('request');
var zlib = require('zlib');
var changes = require('concurrent-couch-follower');
var JSONStream = require('JSONStream');
var es = require('event-stream');
var etag = require('nginx-etag');

var config = require('./configLoader');

var logger = config.logger;

var api = {};

var httpCentralUrl = config.centralURL.replace('https', 'http');
var httpsCentralUrl = httpCentralUrl.replace('http', 'https');
var sequenceFile = path.join(config.attachmentPath, '.sequence');

function verifyShasum(tmpFilePath, tarball, filePath) {
  var hash = crypto.createHash('sha1');
  fs.createReadStream(tmpFilePath).on('data', function(data) {
    hash.update(data);
  })
    .on('end', function() {
      var shasum = hash.digest('hex');
      var targetShasum;
      request({
        // We request ourselves to benefit from JSON caching!
        baseUrl: 'http://localhost:' + config.port,
        url: tarball.name
      })
      // Extract shasum of the correct version
        .pipe(JSONStream.parse(['versions', tarball.version, 'dist', 'shasum']))
        .pipe(es.mapSync(function(data) {
          targetShasum = data;
          if (targetShasum === shasum) {
            logger.trace('Correct shasum %s for file %s.', shasum, tmpFilePath);
            mkdirp(path.dirname(filePath), function() {
              fs.rename(tmpFilePath, filePath);
            });
          } else {
            logger.error('Incorrect shasum %s for file %s (expected: %s): we delete the file.', shasum, tmpFilePath, targetShasum);
            fs.unlink(tmpFilePath);
          }
        }))
        .on('end', function() {
          if (targetShasum === undefined) {
            logger.error('Unavailable shasum for version %s of module %s: we delete the file.', tarball.version, tarball.name);
            fs.unlink(tmpFilePath);
          }
        });
    });
}

function verifyJson(tmpFilePath, filePath) {
  fs.readFile(tmpFilePath, 'utf8', function(err, jsonData) {
    var isJSON = true;
    try {
      var json = JSON.parse(jsonData);
    } catch (e) {
      isJSON = false;
      logger.error('File %s is not a valid JSON: we delete the JSON files.', tmpFilePath, e);
    }
    if (!isJSON || !json.name || !json.versions || !json.time) {
      if (isJSON) {
        logger.error('File %s is a valid JSON but it lacks one property among "name", "versions" and "time".', tmpFilePath);
      }
      fs.unlinkSync(tmpFilePath);
      if (fs.existsSync(filePath)) {
        fs.unlinkSync(filePath);
      }
    } else {
      logger.trace('File %s is a valid JSON.', tmpFilePath);
      var latest = (json['dist-tags']) ? json['dist-tags'].latest : '';
      logger.debug('Saving new version of %s: %s', json.name, latest);
      mkdirp(path.dirname(filePath), function() {
        fs.rename(tmpFilePath, filePath);
      });
    }
  })
}

/**
 * Serve attachment from file system if present, else serve it from central registry (and save it on file system)
 * @param req request
 * @param resp response
 * @param {Tarball} tarball - tarball
 */
api.manageAttachment = function(req, resp, tarball) {
  var fileName = path.basename(req.url);
  var tmpFilePath = path.join(config.attachmentPath, fileName + '.tmp');
  var filePath = path.join(config.attachmentPath, req.url);
  fs.exists(filePath, function(exists) {
    if (exists) {
      logger.debug('%s: serving attachment from file system: %s', req.url, filePath);
      fs.createReadStream(filePath).pipe(resp);
    } else {
      var downloadURL = config.centralURL + req.url;
      logger.debug('%s: downloading, saving and serving attachment from central registry: %s', req.url, downloadURL);
      var couchDBStream = request(downloadURL);
      req.pipe(couchDBStream);
      couchDBStream.pipe(resp);
      var tmpFileStream = fs.createWriteStream(tmpFilePath);
      couchDBStream.pipe(tmpFileStream);

      tmpFileStream.on('finish', function() {
        logger.trace('File %s is completely written', tmpFilePath);
        verifyShasum(tmpFilePath, tarball, filePath);
      });
    }
  });
};

function unzipIfRequired(response) {
  var contentEncoding = response.headers['content-encoding'];
  if (contentEncoding === 'gzip') {
    // Response is a gzip stream, we gunzip it in order to be able to replace content
    response = response.pipe(zlib.createGunzip());
  } else if (contentEncoding === 'deflate') {
    // Response is a deflate stream, we inflate it in order to be able to replace content
    response = response.pipe(zlib.createInflate());
  }
  return response;
}

function proxyToCentralRepositoryAndReplaceUrls(req, resp) {
  var centralCouchDBUrl = config.centralURL;
  logger.debug('%s: forwarding to repository (%s) and responding with attachment URL replacement', req.url, centralCouchDBUrl);
  var centralRegistry = {
    baseUrl: centralCouchDBUrl,
    url: req.url
  };
  req.pipe(request(centralRegistry)).on('response', function(response) {
    response = unzipIfRequired(response);
    response
      .pipe(es.replace(httpsCentralUrl, config.proxyURL))
      .pipe(es.replace(httpCentralUrl, config.proxyURL))
      .pipe(resp);
  });
}
/**
 * Serve JSON of a module from local (or central) registry with attachment URL replacement
 * @param req request
 * @param resp response
 * @param {Module} module - module
 */
api.manageModule = function(req, resp, module) {
  // TODO: We do NOT handle requests with version for now
  if (module.version) {
    proxyToCentralRepositoryAndReplaceUrls(req, resp);
  } else {
    var tmpFilePath = path.join(config.attachmentPath, module.name + '.json.tmp');
    var filePath = path.join(config.attachmentPath, module.name + '.json');
    fs.exists(filePath, function(exists) {
      if (exists) {
        etag.fromFile(filePath, function(err, jsonEtag) {
          var ifNoneMatch = req.headers['if-none-match'];
          if (ifNoneMatch && ifNoneMatch === jsonEtag) {
            logger.debug('%s: client cache up to date. Replying "Not Modified"', req.url);
            resp.writeHead(304);
            resp.end();
          } else {
            logger.debug('%s: serving JSON from file system: %s', req.url, filePath);
            resp.setHeader('etag', jsonEtag);
            fs.createReadStream(filePath)
              .pipe(es.replace(httpsCentralUrl, config.proxyURL))
              .pipe(es.replace(httpCentralUrl, config.proxyURL))
              .pipe(resp);
          }
        });
      } else {
        var downloadURL = config.centralURL + req.url;
        logger.debug('%s: downloading, saving and serving JSON from central registry: %s', req.url, downloadURL);
        req.pipe(request(downloadURL))
          .on('response', function(response) {
            response = unzipIfRequired(response);
            response
              .pipe(es.replace(httpsCentralUrl, config.proxyURL))
              .pipe(es.replace(httpCentralUrl, config.proxyURL))
              .pipe(resp);
            var tmpFileStream = fs.createWriteStream(tmpFilePath);
            response.pipe(tmpFileStream);
            tmpFileStream.on('finish', function() {
              logger.trace('File %s is completely written', tmpFilePath);
              verifyJson(tmpFilePath, filePath);
            })
          });
      }
    });
  }
};

/**
 * Proxy to central repository
 * @param req request
 * @param resp response
 */
api.proxyToCentralRepository = function(req, resp) {
  var centralCouchDBUrl = config.centralURL;
  logger.debug('%s: forwarding to central repository (%s)', req.url, centralCouchDBUrl);
  var centralRegistry = {
    baseUrl: centralCouchDBUrl,
    url: req.url
  };
  req.pipe(request(centralRegistry)).pipe(resp);
};

api.initChangesSync = function() {
  mkdirp.sync(path.dirname(sequenceFile));

  var stream = changes(function(data, done) {
    // Replace "/" by "%2f" for scoped modules
    var moduleName = data.id.replace('/', '%2f');
    var fileName = moduleName + '.json';
    var filePath = path.join(config.attachmentPath, fileName);

    fs.exists(filePath, function(exists) {
      if (exists) {
        logger.debug('Updating %s module (seq: %s)', data.id, data.seq);
        // We can't use data.doc as it could contain invalid semver (e.g.: grunt-0.4.0a). To prevent this problem, we then download the JSON from central registry which retains only valid versions.
        // As change detection and json download are from 2 different server, we add a delay to prevent downloading a not up to date json.
        setTimeout(function() {
          // Delete deprecated file
          fs.unlinkSync(filePath);
          // Request the module from ourselves to trigger json save
          request(
            {
              baseUrl: 'http://localhost:' + config.port,
              url: moduleName
            },
            done
          );
        }, config.delay);
      } else {
        logger.trace('New %s module (seq: %s), but we don\'t use it', data.id, data.seq);
        done();
      }
    });
  }, {
    db: config.syncURL,
    now: true,
    sequence: sequenceFile,
    include_docs: false,
    concurrency: 1
  });

  var seq = stream.sequence();
  if (seq === 0) {
    logger.debug('Starting sync from latest sequence number on %s', config.syncURL);
  } else {
    logger.debug('Restarting sync from %s since sequence number %s', config.syncURL, seq);
  }
};

module.exports = api;
<|endoftext|>


<commit_before>var webpack = require('webpack');
var path = require('path');
var common = require('../webpack.common.config.js');
var assign = require('object-assign');
var BundleTracker = require('webpack-bundle-tracker');

var websiteRoot = path.join(__dirname, 'websiteRoot', 'static');

var adminRoot = path.join(__dirname, 'admin', 'static');

var staticWebsitePath = function(dir) {
    return path.join(websiteRoot, dir);
};

var staticAdminPath = function(dir) {
    return path.join(adminRoot, dir);
};

// [lauren]: how I think adding another file will work
//common.entry.<new file> = staticAdminPath('js/pages/base-page.js');

// Adding bundle tracker to plugins
common.plugins = [
    // Bundle common code between modules
    new webpack.optimize.CommonsChunkPlugin('vendor', 'vendor.js'),
    // Bower support
    new webpack.ResolverPlugin(
        new webpack.ResolverPlugin.DirectoryDescriptionFilePlugin('bower.json', ['main'])
    ),
    // Make jQuery available in all modules without having to do require('jquery')
    new webpack.ProvidePlugin({
        $: 'jquery',
        jQuery: 'jquery'
    }),
    // Slight hack to make sure that CommonJS is always used
    new webpack.DefinePlugin({
        'define.amd': false
    }),
    // for using webpack with Django
    new BundleTracker({filename: './webpack-stats.json'}),
];

common.output = {
    path: './static/public/js/',
    // publicPath: '/static/', // used to generate urls to e.g. images
    filename: '[name].js',
    sourcePrefix: ''
};

module.exports = assign(common, {
    debug: true,
    devtool: 'source-map'
});
<commit_msg>update admin config file<commit_after>var webpack = require('webpack');
var path = require('path');
var common = require('../webpack.common.config.js');
var assign = require('object-assign');
var BundleTracker = require('webpack-bundle-tracker');

var websiteRoot = path.join(__dirname, 'websiteRoot', 'static');

var adminRoot = path.join(__dirname, 'static');

var staticWebsitePath = function(dir) {
    return path.join(websiteRoot, dir);
};

var staticAdminPath = function(dir) {
    return path.join(adminRoot, dir);
};

common.entry = {
    'app.min':staticAdminPath('js/app.min.js'),
};

// Adding bundle tracker to plugins
common.plugins = [
    // Bundle common code between modules
    new webpack.optimize.CommonsChunkPlugin('vendor', 'vendor.js'),
    // Bower support
    new webpack.ResolverPlugin(
        new webpack.ResolverPlugin.DirectoryDescriptionFilePlugin('bower.json', ['main'])
    ),
    // Make jQuery available in all modules without having to do require('jquery')
    new webpack.ProvidePlugin({
        $: 'jquery',
        jQuery: 'jquery'
    }),
    // Slight hack to make sure that CommonJS is always used
    new webpack.DefinePlugin({
        'define.amd': false
    }),
    // for using webpack with Django
    new BundleTracker({filename: './webpack-stats.json'}),
];

common.output = {
    path: './static/public/js/',
    // publicPath: '/static/', // used to generate urls to e.g. images
    filename: '[name].js',
    sourcePrefix: ''
};

module.exports = assign(common, {
    debug: true,
    devtool: 'source-map'
});<|endoftext|>


<commit_before>/*
 * Created by Martin Giger
 * This Source Code Form is subject to the terms of the Mozilla Public License,
 * v. 2.0. If a copy of the MPL was not distributed with this file, You can
 * obtain one at http://mozilla.org/MPL/2.0/.
 */

function Overlay(el) {
        this._overlay = el;
        this.hide = this.hide.bind(this);
        this._prevent = this._prevent.bind(this);
        this._overlay.addEventListener("click", this.hide, false);
        this.getDialog().addEventListener("click", this._prevent, true);
        this._overlay.classList.add("so-backdrop");
}

Overlay.prototype = {
    _overlay: "",
    getMain: function() {
        var main = document.getElementsByTagName("MAIN");
        if(main.length == 0) {
            main = document.querySelector("[role='main']");
        }
        else {
            main = main[0];
        }
        return main;
    },
    getDialog: function() {
        var dialog = this._overlay.getElementsByTagName("DIALOG");
        if(dialog.length == 0) {
            dialog = this._overlay.querySelector("[role='dialog']");
        }
        else {
            dialog = dialog[0];
        }
        return dialog;
    },
    show: function() {
        this._overlay.removeAttribute("hidden");
        this.getMain().setAttribute("aria-hidden", true);
        this.getDialog().focus();
    },
    hide: function() {
        this._overlay.setAttribute("hidden", true);
        this.getMain().setAttribute("aria-hidden", false);
        this.getMain().focus();
    },
    _prevent: function(e) {
        if("stopPropagation" in e)
            e.stopPropagation();
        else
            e.preventBubble();
    }
};
<commit_msg>Tabindex<commit_after>/*
 * Created by Martin Giger
 * This Source Code Form is subject to the terms of the Mozilla Public License,
 * v. 2.0. If a copy of the MPL was not distributed with this file, You can
 * obtain one at http://mozilla.org/MPL/2.0/.
 */

function Overlay(el) {
        this._overlay = el;
        this.hide = this.hide.bind(this);
        this._prevent = this._prevent.bind(this);
        this._overlay.addEventListener("click", this.hide, false);
        this.getDialog().addEventListener("click", this._prevent, true);
        this._overlay.classList.add("so-backdrop");
}

Overlay.prototype = {
    _overlay: "",
    getMain: function() {
        var main = document.getElementsByTagName("MAIN");
        if(main.length == 0) {
            main = document.querySelector("[role='main']");
        }
        else {
            main = main[0];
        }
        return main;
    },
    getDialog: function() {
        var dialog = this._overlay.getElementsByTagName("DIALOG");
        if(dialog.length == 0) {
            dialog = this._overlay.querySelector("[role='dialog']");
        }
        else {
            dialog = dialog[0];
        }
        return dialog;
    },
    show: function() {
        this._overlay.removeAttribute("hidden");
        this.getMain().setAttribute("aria-hidden", true);
        var dialog = this.getDialog();
        dialog.setAttribute("tabindex", 0);
        dialog.focus();
    },
    hide: function() {
        this._overlay.setAttribute("hidden", true);
        var main = this.getMain();
        main.setAttribute("aria-hidden", false);
        main.focus();
        this.getDialog().removeAttribute("tabindex");
    },
    _prevent: function(e) {
        if("stopPropagation" in e)
            e.stopPropagation();
        else
            e.preventBubble();
    }
};
<|endoftext|>


<commit_before>import MarkupIt, { BLOCKS, INLINES, TABLE_ALIGN, MARKS, CONTAINERS, VOID } from 'markup-it';
import markdown from 'markup-it/lib/markdown';
import React, { Component, PropTypes } from 'react';
import Slate, { Editor, State, Text, Inline, Block } from 'slate';

import schema from '../constants/SlateSchema';

const OPTIONS = { normalize: false };
const linkRegex = /\[([^\]]*)\]\(([^\)]*)\)/;

class SlateEditor extends Component {
  static propTypes = {
    note: PropTypes.object,
    onChange: PropTypes.func.isRequired,
  };

  state = {
    state: deserializeToState(''),
  };

  componentWillReceiveProps(nextProps) {
    if (!nextProps.note.isMarkdown) {
      console.error('Only markdown note is supported');
      return;
    }

    this.setState({
      state: deserializeToState(nextProps.note.content),
    });
  }

  render() {
    return (

        <Editor
          className="slate-editor markdown-body"
          onBlur={this.onBlur}
          onChange={this.onChange}
          onDocumentChange={this.onDocumentChange}
          onKeyDown={this.onKeyDown}
          onPaste={this.onPaste}
          onSelectionChange={this.onSelectionChange}
          placeholder="Enter text here..."
          ref="slate"
          schema={schema}
          state={this.state.state}
        />

    )
  }

  onPaste = () => {
    // 粘贴的图片都上传
  }

  onSelectionChange = (selection, state) => {
    console.log(prettify(state));
    const { startBlock, startOffset, startText } = state;
    const parent = state.document.getParent(startText.key);
    if (parent.type !== INLINES.LINK) {
      // 有没有编辑的事件，放在那更直观
      // 刚刚编辑完一个link，将markdown源码解析成link
      const match = linkRegex.exec(startText.text);
      if (match && match.length === 3) {
        const from = match.index, to = match.index + match[0].length;
        const nodes = [];
        nodes.push(Inline.create({
          type: INLINES.LINK,
          data: { href: match[2] },
          isVoid: false,
          nodes: [
            // 直接放两个Text，会被合并
            Text.createFromString(`[${match[1]}]`),
            Text.createFromString(`(${match[2]})`),
          ],
        }));

        console.log(parent.type, prettify(selection));
        console.log(match[1].length + match[2].length + 4);
        // 不能放在selection change里，selection change早于oninput
        const nextState = state.transform()
          // .deleteAtRange(selection)
          // .insertInlineAtRange(selection, Inline.create({
          //   data: { href: 'you' },
          //   type: INLINES.LINK,
          //   nodes: [ Text.createFromString('fuck') ]
          // }))
          // 这个版本更简单一些
          .deleteBackward(match[1].length + match[2].length + 4)
          // .insertInline(Inline.create({
          //   data: { href: match[2] },
          //   type: INLINES.LINK,
          //   nodes: [ Text.createFromString(match[1]) ]
          // }))
          .apply(OPTIONS);
        this.setState({ state: nextState });
      }
    } else {
      // 用户进入一个link，将link替换成markdown源码
      if (!linkRegex.exec(parent.text)) {
        console.log('enter link', parent.text, prettify(parent));
        const nextState = state.transform()
          .removeNodeByKey(startText.key)
          .insertNodeByKey(parent.key, 0, Text.createFromString(`[${parent.text}](${parent.data.get('href')})`))
          .apply(OPTIONS);
        this.setState({ state: nextState });
      }
    }
    if (this.prevParent && this.prevParent!== parent && this.prevParent.type === INLINES.LINK) {
      // 离开一个LINK，转成anchor
      const match = linkRegex.exec(this.prevParent.text)
      console.log('exit link', this.prevParent.text, prettify(this.prevParent));
      if (!match) {
        console.error('No alias and href found in link');
        // return;
      } else {
        const alias = match[1] || 'alis';
        const href = match[2] || 'href';

        // 凎，就没有删除所有子元素的方法吗
        const nextState = state.transform()
          .setNodeByKey(this.prevParent.key, { data: { href } })
          .removeNodeByKey(this.prevStartText.key)
          .insertNodeByKey(this.prevParent.key, 0, Text.createFromString(alias))
          .apply(OPTIONS);
        this.setState({ state: nextState });
      }
    }
    console.log(parent.key, parent.type);
    this.prevStartText = startText;
    this.prevParent = parent;
  }

  onBlur = () => {
    const text = serializeState(this.state.state);
    if (text !== this.props.note.content) {
      this.props.onChange(text);
    }
  }

  onChange = (state) => {
    this.setState({ state })
  }

  onKeyDown = (e, data, state) => {
    switch (data.key) {
      case 'space': return this.onSpace(e, state)
      case 'backspace': return this.onBackspace(e, state)
      case 'enter': return this.onEnter(e, state)
    }
  }

  /**
   * Get the block type for a series of auto-markdown shortcut `chars`.
   *
   * @param {String} chars
   * @return {String} block
   */
  getType = (chars) => {
    switch (chars) {
      case '*':
      case '-':
      case '+': return BLOCKS.LIST_ITEM;
      case '>': return BLOCKS.BLOCKQUOTE;
      case '#': return BLOCKS.HEADING_1;
      case '##': return BLOCKS.HEADING_2;
      case '###': return BLOCKS.HEADING_3;
      case '####': return BLOCKS.HEADING_4;
      case '#####': return BLOCKS.HEADING_5;
      case '######': return BLOCKS.HEADING_6;
      default: return null
    }
  }

  /**
   * On space, if it was after an auto-markdown shortcut, convert the current
   * node into the shortcut's corresponding type.
   *
   * @param {Event} e
   * @param {State} state
   * @return {State or Null} state
   */
  onSpace = (e, state) => {
    if (state.isExpanded) return
    const { startBlock, startOffset } = state
    const chars = startBlock.text.slice(0, startOffset).replace(/\s*/g, '')
    const type = this.getType(chars)

    if (!type) return
    if (type == BLOCKS.LIST_ITEM && startBlock.type == BLOCKS.LIST_ITEM) return
    e.preventDefault()

    let transform = state
      .transform()
      .setBlock(type)

    if (type == BLOCKS.LIST_ITEM) transform.wrapBlock(BLOCKS.UL_LIST)

    state = transform
      .extendToStartOf(startBlock)
      .delete()
      .apply()

    return state
  }

  /**
   * On backspace, if at the start of a non-paragraph, convert it back into a
   * paragraph node.
   *
   * @param {Event} e
   * @param {State} state
   * @return {State or Null} state
   */
  onBackspace = (e, state) => {
    if (state.isExpanded) return
    if (state.startOffset != 0) return
    const { startBlock } = state
    if (startBlock.type == BLOCKS.PARAGRAPH) return
    if (startBlock.length === 0 && startBlock.text === '') {
      return
    }
    e.preventDefault()

    let transform = state
      .transform()
      .setBlock(BLOCKS.PARAGRAPH)

    if (startBlock.type == BLOCKS.LIST_ITEM) transform.unwrapBlock(BLOCKS.UL_LIST)

    state = transform.apply()
    return state
  }

  /**
   * On return, if at the end of a node type that should not be extended,
   * create a new paragraph below it.
   *
   * @param {Event} e
   * @param {State} state
   * @return {State or Null} state
   */
  onEnter = (e, state) => {
    if (state.isExpanded) return
    const { startBlock, startOffset, endOffset } = state
    if (startOffset == 0 && startBlock.length == 0) return this.onBackspace(e, state)
    if (endOffset != startBlock.length) return

    if (
      startBlock.type != BLOCKS.HEADING_1 &&
      startBlock.type != BLOCKS.HEADING_2 &&
      startBlock.type != BLOCKS.HEADING_3 &&
      startBlock.type != BLOCKS.HEADING_4 &&
      startBlock.type != BLOCKS.HEADING_5 &&
      startBlock.type != BLOCKS.HEADING_6 &&
      startBlock.type != BLOCKS.BLOCKQUOTE
    ) {
      return
    }

    e.preventDefault()
    return state
      .transform()
      .splitBlock()
      .setBlock(BLOCKS.PARAGRAPH)
      .apply()
  }
}

/**
 * Transform Slate.state to markdown
 */
function serializeState(state) {
  const text = MarkupIt.State.create(markdown).serializeDocument(state.document);
  return text;
}

/**
 * Transform markdown to Slate.state
 */
function deserializeToState(text) {
  const document = MarkupIt.State.create(markdown).deserializeToDocument(text);
  const state = Slate.State.create({ document });
  // trim empty spans
  const blocks = state.document.getBlocks();
  const transform = state.transform();
  blocks.forEach(block => {
    block.nodes.forEach(node => {
      if (node.text === '' && node.key !== '0') {
        transform.removeNodeByKey(node.key, OPTIONS);
      }
    });
  });
  return transform.apply({ normalize: false });
}

function prettify(obj) {
  return JSON.parse(JSON.stringify(obj));
}

function insertBefore(state, after, nodes) {
  const parent = state.document.getParent(after);
  const index = parent.nodes.find(node => node.key === node.key);

  const transform = state.transform();
  transform.removeNodeByKey(after.key);
  nodes.reverse().forEach((newNode, i) => transform.insertNodeByKey(parent.key, index, newNode));
  const nextState = transform.apply(OPTIONS);
  console.log(prettify(nextState.document));
  return nextState;
}

export default SlateEditor
<commit_msg>Update state with Selection Transforms<commit_after>import MarkupIt, { BLOCKS, INLINES, TABLE_ALIGN, MARKS, CONTAINERS, VOID } from 'markup-it';
import markdown from 'markup-it/lib/markdown';
import React, { Component, PropTypes } from 'react';
import Slate, { Editor, State, Text, Inline, Block } from 'slate';

import schema from '../constants/SlateSchema';

const OPTIONS = { normalize: false };
const linkRegex = /\[([^\]]*)\]\(([^\)]*)\)/;

class SlateEditor extends Component {
  static propTypes = {
    note: PropTypes.object,
    onChange: PropTypes.func.isRequired,
  };

  state = {
    state: deserializeToState(''),
  };

  componentWillReceiveProps(nextProps) {
    if (!nextProps.note.isMarkdown) {
      console.error('Only markdown note is supported');
      return;
    }

    this.setState({
      state: deserializeToState(nextProps.note.content),
    });
  }

  render() {
    return (

        <Editor
          className="slate-editor markdown-body"
          onBlur={this.onBlur}
          onChange={this.onChange}
          onDocumentChange={this.onDocumentChange}
          onKeyDown={this.onKeyDown}
          onPaste={this.onPaste}
          onSelectionChange={this.onSelectionChange}
          placeholder="Enter text here..."
          ref="slate"
          schema={schema}
          state={this.state.state}
        />

    )
  }

  onPaste = () => {
    // 粘贴的图片都上传
  }

  onSelectionChange = (selection, state) => {
    const { startBlock, startOffset, startText } = state;
    let parent = state.document.getParent(startText.key);
    // console.log('1', startBlock.type, parent.type);
    console.log(prettify(selection), prettify(state.document));
    if (parent.type !== INLINES.LINK && startText.text !== '\n') {
      // 刚刚编辑完一个link，将markdown源码解析成link
      const match = linkRegex.exec(startText.text);
      if (match) {
        const nextState = state.transform()
          .extendToStartOf(startText)
          .delete()
          .insertInline(Inline.create({
            data: { href: match[2] },
            type: INLINES.LINK,
            nodes: [ Text.createFromString(`[${match[1]}](${match[2]})`) ]
          }))
          .apply(OPTIONS);
        setTimeout(() => { // 谜之setTimeout
          this.setState({ state: nextState });
        });
        return;
      }
    } else {
      if (state.startText.text === '\n') {
        // 遇到换行，将selection移动到换行之前的node
        state = state.transform()
          .collapseToStartOfPreviousText()
          .apply(OPTIONS);
      }
      parent = state.document.getParent(state.startText.key);
      // 用户进入一个link，将link替换成markdown源码
      if (!linkRegex.exec(parent.text)) { // 还没有被转成源码
        const nextState = state.transform()
          .extendToStartOf(state.startText)
          .delete()
          .insertInline(Inline.create({
            data: { href: parent.data.get('href') },
            type: INLINES.LINK,
            nodes: [ Text.createFromString(`[${parent.text}](${parent.data.get('href')})`) ]
          }))
          .apply(OPTIONS);
        setTimeout(() => {
          this.setState({ state: nextState });
        });
      }
    }
    if (this.prevParent && this.prevParent !== parent && this.prevParent.type === INLINES.LINK) {
      // 离开一个LINK，转成anchor
      // const match = linkRegex.exec(this.prevParent.text)
      // console.log('exit link', this.prevParent.text, prettify(this.prevParent));
      // if (!match) {
      //   console.error('No alias and href found in link');
      //   // return;
      // } else {
      //   const alias = match[1] || 'alis';
      //   const href = match[2] || 'href';
      //   // 没有删除所有子元素的方法?
      //   const nextState = state.transform()
      //     .setNodeByKey(this.prevParent.key, { data: { href } })
      //     .removeNodeByKey(this.prevStartText.key)
      //     .insertNodeByKey(this.prevParent.key, 0, Text.createFromString(alias))
      //     .apply(OPTIONS);
      //   this.setState({ state: nextState });
      // }
    }
    this.prevStartText = startText;
    this.prevParent = parent;
  }

  onBlur = () => {
    const text = serializeState(this.state.state);
    if (text !== this.props.note.content) {
      this.props.onChange(text);
    }
  }

  onChange = (state) => {
    const { startBlock, startOffset, startText, selection } = state;
    const parent = state.document.getParent(startText.key);
    if (parent.type !== INLINES.LINK) {
    }
    this.setState({ state });
  }

  onKeyDown = (e, data, state) => {
    switch (data.key) {
      case 'space': return this.onSpace(e, state)
      case 'backspace': return this.onBackspace(e, state)
      case 'enter': return this.onEnter(e, state)
    }
  }

  /**
   * Get the block type for a series of auto-markdown shortcut `chars`.
   *
   * @param {String} chars
   * @return {String} block
   */
  getType = (chars) => {
    switch (chars) {
      case '*':
      case '-':
      case '+': return BLOCKS.LIST_ITEM;
      case '>': return BLOCKS.BLOCKQUOTE;
      case '#': return BLOCKS.HEADING_1;
      case '##': return BLOCKS.HEADING_2;
      case '###': return BLOCKS.HEADING_3;
      case '####': return BLOCKS.HEADING_4;
      case '#####': return BLOCKS.HEADING_5;
      case '######': return BLOCKS.HEADING_6;
      default: return null
    }
  }

  /**
   * On space, if it was after an auto-markdown shortcut, convert the current
   * node into the shortcut's corresponding type.
   *
   * @param {Event} e
   * @param {State} state
   * @return {State or Null} state
   */
  onSpace = (e, state) => {
    if (state.isExpanded) return
    const { startBlock, startOffset } = state
    const chars = startBlock.text.slice(0, startOffset).replace(/\s*/g, '')
    const type = this.getType(chars)

    if (!type) return
    if (type == BLOCKS.LIST_ITEM && startBlock.type == BLOCKS.LIST_ITEM) return
    e.preventDefault()

    let transform = state
      .transform()
      .setBlock(type)

    if (type == BLOCKS.LIST_ITEM) transform.wrapBlock(BLOCKS.UL_LIST)

    state = transform
      .extendToStartOf(startBlock)
      .delete()
      .apply()

    return state
  }

  /**
   * On backspace, if at the start of a non-paragraph, convert it back into a
   * paragraph node.
   *
   * @param {Event} e
   * @param {State} state
   * @return {State or Null} state
   */
  onBackspace = (e, state) => {
    if (state.isExpanded) return
    if (state.startOffset != 0) return
    const { startBlock } = state
    if (startBlock.type == BLOCKS.PARAGRAPH) return
    if (startBlock.length === 0 && startBlock.text === '') {
      return
    }
    e.preventDefault()

    let transform = state
      .transform()
      .setBlock(BLOCKS.PARAGRAPH)

    if (startBlock.type == BLOCKS.LIST_ITEM) transform.unwrapBlock(BLOCKS.UL_LIST)

    state = transform.apply()
    return state
  }

  /**
   * On return, if at the end of a node type that should not be extended,
   * create a new paragraph below it.
   *
   * @param {Event} e
   * @param {State} state
   * @return {State or Null} state
   */
  onEnter = (e, state) => {
    if (state.isExpanded) return
    const { startBlock, startOffset, endOffset } = state
    if (startOffset == 0 && startBlock.length == 0) return this.onBackspace(e, state)
    if (endOffset != startBlock.length) return

    if (
      startBlock.type != BLOCKS.HEADING_1 &&
      startBlock.type != BLOCKS.HEADING_2 &&
      startBlock.type != BLOCKS.HEADING_3 &&
      startBlock.type != BLOCKS.HEADING_4 &&
      startBlock.type != BLOCKS.HEADING_5 &&
      startBlock.type != BLOCKS.HEADING_6 &&
      startBlock.type != BLOCKS.BLOCKQUOTE
    ) {
      return
    }

    e.preventDefault()
    return state
      .transform()
      .splitBlock()
      .setBlock(BLOCKS.PARAGRAPH)
      .apply()
  }
}

/**
 * Transform Slate.state to markdown
 */
function serializeState(state) {
  const text = MarkupIt.State.create(markdown).serializeDocument(state.document);
  return text;
}

/**
 * Transform markdown to Slate.state
 */
function deserializeToState(text) {
  const document = MarkupIt.State.create(markdown).deserializeToDocument(text);
  const state = Slate.State.create({ document });
  // trim empty spans
  const blocks = state.document.getBlocks();
  const transform = state.transform();
  blocks.forEach(block => {
    block.nodes.forEach(node => {
      if (node.text === '' && node.key !== '0') {
        transform.removeNodeByKey(node.key);
      }
    });
    // if (block.text === '') {
    //   transform.removeNodeByKey(block.key);
    // }
  });
  return transform.apply(OPTIONS);
}

function prettify(obj) {
  return JSON.parse(JSON.stringify(obj));
}

export default SlateEditor
<|endoftext|>


<commit_before>// Dependencies
var Url = require("url")
  , Fs  = require("fs")
  , Path = require("path")
  , Events = require("events")
  , Debug = require("bug-killer")
  , RegexParser = require("regex-parser")
  , Mime = require("mime")
  , Domain = require("domain")
  ;

/**
 * Api
 * Attaches Statique methods to self
 *
 * @name Api
 * @function
 * @param {Object} self Statique instance
 * @return {Object} Statique instance
 */
var Api = module.exports = function (self) {

    /**
     * Sets the root of the public folder.
     *
     * @name server
     * @function
     * @param {Object} options an object containing the following fields:
     *  - root: string representing the absolute path to the public folder.
     *  - cache: the number of seconds of cache
     * @return {Object} Statique instance
     */
    self.server = function (options) {

        if (typeof options === "string") {
            options = {
                root: options
            }
        }
        if (options.root.slice(-1) !== "/") {
            options.root += "/";
        }

        self._root = options.root;
        self._cache = typeof options.cache === "number" ? options.cache : 3600;

        return self;
    };

    /**
     * Sets the routes of the website.
     *
     * @name setRoutes
     * @function
     * @param {Object} routes an object containing fields and values in the
     * following format:
     *  {
     *      "/":       "/html/index.html"
     *    , "/foo/":   { url: "/html/foo.html" }
     *    , "/another-foo": "/html/myfoo.html"
     *    , "/some/api": function (req, res) {
     *          res.end("Hello World!");
     *      }
     *  }
     *  See test file for more examples.
     * @return {Object} Statique instance
     */
    self.setRoutes = function (routes) {

        self._routes = routes;
        self._regexpRoutes = [];

        for (var r in routes) {
            var cRoute = routes[r];
            if ((cRoute.type || "").toLowerCase() === "regexp") {
                if ((cRoute.regexp || {}).constructor.name !== "RegExp") {
                    cRoute.regexp = RegexParser(r);
                }
                self._regexpRoutes.push(cRoute);
                delete cRoute[r];
            }
        }

        return self;
    };

    /**
     * Sets the error pages
     *
     * @name setErrors
     * @function
     * @param {Object} errorRoutes An object with the error codes and their paths to the HTML files
     * @return {Object} Statique instance
     */
    self.setErrors = function (errorRoutes) {
        self._errors = errorRoutes;
        return self;
    };

    /**
     * Gets the route by providing an @url
     *
     * @name getRoute
     * @function
     * @param {String} url a string representing the url of the page that
     * must be served
     * @return {Object} the route object that contains the following fields:
     *  - url
     *  - handler
     *  - _data (original route from config)
     */
    self.getRoute = function (url) {

        var route = {};

        // remove slash at the end of the string
        if (url.slice (-1) === "/") {
            url = url.substring(0, url.length - 1);
        }

        // get the route that was set in the configuration
        var routeObj = self._routes[url] || self._routes[url + "/"] || null;
        route.url = (routeObj || {}).url || routeObj;
        route.reqUrl = route.url || url;
        route._data = routeObj;

        // Handle regexp routes
        for (var i = 0, cRegexpRoute; i < self._regexpRoutes.length; ++i) {
            cRegexpRoute = self._regexpRoutes[i];
            if (cRegexpRoute.regexp.test(url)) {
                for (var k in cRegexpRoute) {
                    route[k] = cRegexpRoute[k];
                }
                break;
            }
        }

        // handle url as function
        if (typeof route.url === "function") {
            route.handler = route.url;
            delete route.url;
        }

        if (routeObj && typeof routeObj.handler === "function") {
            routeObj.handler = routeObj.handler;
        }

        return route;
    };

    /**
     * Checks if a route exists.
     *
     * @name exists
     * @function
     * @param {Object} req the request object
     * @return {Boolean} true, if the route was found, else false
     */
    self.exists = function (req) {
        return Boolean(self.getRoute(Url.parse(req.url).pathname));
    };

    /**
     * Reads the file and callbacks the content.
     *
     * @name readFile
     * @function
     * @param {String} file the relative path to the file
     * @param {Function} callback the callback function that will be called with an
     * err (first argument) and the content of the file (second argument)
     * @return {Buffer} the raw buffer
     */
    self.readFile = function (file, callback) {
        return Fs.readFile(self._root + file, function (err, buffer) {
            if (err) { return callback(err); }
            callback(null, buffer.toString())
        });
    };

    /**
     * Serves the HTML file by providing the @req and @res parameters
     *
     * @name serve
     * @function
     * @param {Object} req the request object
     * @param {Object} res the response object
     * @return {Object} the Statique instance
     */
    self.serve = function (req, res) {
        return self.serveRoute(undefined, req, res);
    };

    /**
     * This function is used for sending custom status messages and content
     * If the @content parameter is not provided or is not a string, the response
     * will not be ended. The status code and the headers will be set
     *
     * @name sendRes
     * @function
     * @param {Object} res the response object
     * @param {Number} statusCode the response status code
     * @param {String} mimeType the response mime type
     * @param {String} content the content that you want to send via response
     * @param {Object} otherHeaders Aditional headers that will be merged with
     * the basic ones. They have greater priority than basic headers.
     * @return {Object} the Statique instance
     */
    self.sendRes = function (res, statusCode, mimeType, content, otherHeaders) {

        var headers = {
            "Content-Type": mimeType || "plain/text"
          , "Server": "Statique Server"
        };

        for (var h in otherHeaders) {
            headers[h] = otherHeaders[h] === undefined ? headers[h] : otherHeaders[h];
        }

        res.writeHead(statusCode, headers);

        if (typeof content === "string" || content instanceof Buffer) {
            res.end(content);
        }

        return self;
    };

    /**
     * Serves a file
     *
     * @name serveFile
     * @function
     * @param {String|Object} path The path to the file that should be served or the
     * route object
     * @param {Number} statusCode The response status code (default: 200)
     * @param {Object} res The response object
     * @param {Object} req The request object
     * @param {Object} additionalHeaders Additional headers that should be sent
     * @param {String} customRoot Path to the custom root (e.g. "/")
     * @return {Object} Statique instance
     */
    self.serveFile = function (path, statusCode, res, req, additionalHeaders, customRoot) {

        res = Object(res);
        req = Object(req);
        req.headers = req.headers || {};

        if (typeof path === "string") {
            path = {
                reqUrl: path
            };
        }

        var fullPath = (customRoot ? customRoot : self._root) + (path.url || path.reqUrl);

        try {
            stats = Fs.lstatSync(fullPath);
        } catch (e) {
            self.error(req, res, 404, "Not found");
            return self;
        }

        // no file, no fun
        if (!stats.isFile()) { return self; }

        // cache stuff
        var mtime = Date.parse(stats.mtime)
          , clientETag = req.headers['if-none-match']
          , clientMTime = Date.parse(req.headers['if-modified-since'])
          , contentType = Mime.lookup(path.reqUrl)
          , headers = {
                "Etag": JSON.stringify([stats.ino, stats.size, mtime].join('-'))
              , "Date": (new Date()).toUTCString()
              , "Last-Modified": (new Date(stats.mtime)).toUTCString()
              , "Content-Type": contentType
              , "Content-Length": stats.size
            }
          ;

        // file is cached
        if ((clientMTime  || clientETag) &&
            (!clientETag  || clientETag === headers['Etag']) &&
            (!clientMTime || clientMTime >= mtime)) {
            // 304 response should not contain entity headers
            ['Content-Encoding',
             'Content-Language',
             'Content-Length',
             'Content-Location',
             'Content-MD5',
             'Content-Range',
             'Content-Type',
             'Expires',
             'Last-Modified'].forEach(function(entityHeader) {
                delete headers[entityHeader];
            });

            for (var aH in additionalHeaders) {
                headers[aH] = additionalHeaders[aH] || headers[aH];
            }

            self.sendRes(res, 304, contentType, null, headers);
            res.end();
            return self;
        }

        // Set cache-control  header
        headers["cache-control"] = "max-age=" + self._cache;
        for (var aH in additionalHeaders) {
            headers[aH] = additionalHeaders[aH] || headers[aH];
        }

        // file should cached
        self.sendRes(res, statusCode || 200, contentType, stats.size ? null : "", headers)

        if (stats.size) {
            var fileStream = Fs.createReadStream(fullPath);
            fileStream.pipe(res);
        }

        return self;
    };

    /**
     * Serves a provided route.
     *
     * @name serveRoute
     * @function
     * @param {String} route The route that should be served
     * @param {Object} req The request object
     * @param {Object} res The response object
     * @return {Object} The Statique instance
     */
    self.serveRoute = function (route, req, res) {

        var parsedUrl = Url.parse(req.url)
          , routeToServe = self.getRoute(
                route || parsedUrl.pathname
            ) || parsedUrl.pathname
          , stats = null
          , method = req.method.toLowerCase()
          , form = {
                data: ""
              , error: ""
              , _emitter: new Events.EventEmitter()
            }
          ;

        req.on("data", function (chunk) {
            form.data += chunk;
        });

        req.on("error", function (err) {
            form.error += err;
        });

        req.on("end", function () {
            form._emitter.emit("done", form);
        });

        var d = Domain.create();
        d.on("error", function(err) {
            self.emit("error", err);
            self.error(req, res, 500, "Internal Server Error");
            Debug.log(e.stack, "error");
        });

        // Add request and response objects
        d.add(req);
        d.add(res);

        // Run handlers
        if (routeToServe.url && typeof routeToServe.url[method] === "function") {
            d.run(function() {
                routeToServe.url[method](req, res, form._emitter);
            });
            return self;
        }

        if (typeof routeToServe.handler === "function") {
            d.run(function() {
                routeToServe.handler(req, res, form._emitter);
            });
            return self;
        }

        // Not a handler, try to serve the file
        return self.serveFile(routeToServe, null, res, req);
    };

    /**
     * Redirects the user to the new url passed in the second argument.
     *
     * @name redirect
     * @function
     * @param {String} res The response object
     * @param {String} newUrl The new url where the user should be redirected
     * @return {Object} Statique instance
     */
    self.redirect = function (res, newUrl) {
        return self.sendRes(res, 301, "text", "Redirecting", {
            "location": newUrl
        });
    };

    /**
     * Sends an error to client
     *
     * @name error
     * @function
     * @param {Object} req The request object
     * @param {Object} res The response object
     * @param {Number} errCode The error code
     * @param {String} errMessage The error message
     * @return {Object} Statique instance
     */
    self.error = function (req, res, errCode, errMessage) {
        var configErrors = Object(self._errors)
          , errPage = configErrors[errCode]
          ;

        if (errPage) {
            return self.serveFile(errPage, errCode, res, req, {
                "cache-control": "no-cache"
            });
        }

        return self.sendRes(res, errCode, "text", errMessage);
    };

    return self;
};
<commit_msg>Various fixes<commit_after>// Dependencies
var Url = require("url")
  , Fs  = require("fs")
  , Path = require("path")
  , Events = require("events")
  , Debug = require("bug-killer")
  , RegexParser = require("regex-parser")
  , Mime = require("mime")
  , Domain = require("domain")
  ;

/**
 * Api
 * Attaches Statique methods to self
 *
 * @name Api
 * @function
 * @param {Object} self Statique instance
 * @return {Object} Statique instance
 */
var Api = module.exports = function (self) {

    /**
     * Sets the root of the public folder.
     *
     * @name server
     * @function
     * @param {Object} options an object containing the following fields:
     *  - root: string representing the absolute path to the public folder.
     *  - cache: the number of seconds of cache
     * @return {Object} Statique instance
     */
    self.server = function (options) {

        if (typeof options === "string") {
            options = {
                root: options
            }
        }
        if (options.root.slice(-1) !== "/") {
            options.root += "/";
        }

        self._root = options.root;
        self._cache = typeof options.cache === "number" ? options.cache : 3600;

        return self;
    };

    /**
     * Sets the routes of the website.
     *
     * @name setRoutes
     * @function
     * @param {Object} routes an object containing fields and values in the
     * following format:
     *  {
     *      "/":       "/html/index.html"
     *    , "/foo/":   { url: "/html/foo.html" }
     *    , "/another-foo": "/html/myfoo.html"
     *    , "/some/api": function (req, res) {
     *          res.end("Hello World!");
     *      }
     *  }
     *  See test file for more examples.
     * @return {Object} Statique instance
     */
    self.setRoutes = function (routes) {

        self._routes = routes;
        self._regexpRoutes = [];

        for (var r in routes) {
            var cRoute = routes[r];
            if ((cRoute.type || "").toLowerCase() === "regexp") {
                if ((cRoute.regexp || {}).constructor.name !== "RegExp") {
                    cRoute.regexp = RegexParser(r);
                }
                self._regexpRoutes.push(cRoute);
                delete cRoute[r];
            }
        }

        return self;
    };

    /**
     * Sets the error pages
     *
     * @name setErrors
     * @function
     * @param {Object} errorRoutes An object with the error codes and their paths to the HTML files
     * @return {Object} Statique instance
     */
    self.setErrors = function (errorRoutes) {
        self._errors = errorRoutes;
        return self;
    };

    /**
     * Gets the route by providing an @url
     *
     * @name getRoute
     * @function
     * @param {String} url a string representing the url of the page that
     * must be served
     * @return {Object} the route object that contains the following fields:
     *  - url
     *  - handler
     *  - _data (original route from config)
     */
    self.getRoute = function (url) {

        var route = {};

        // remove slash at the end of the string
        if (url.slice (-1) === "/") {
            url = url.substring(0, url.length - 1);
        }

        // get the route that was set in the configuration
        var routeObj = self._routes[url] || self._routes[url + "/"] || null;
        route.url = (routeObj || {}).url || routeObj;
        route.reqUrl = route.url || url;
        route._data = routeObj;

        // Handle regexp routes
        for (var i = 0, cRegexpRoute; i < self._regexpRoutes.length; ++i) {
            cRegexpRoute = self._regexpRoutes[i];
            if (cRegexpRoute.regexp.test(url)) {
                for (var k in cRegexpRoute) {
                    route[k] = cRegexpRoute[k];
                }
                break;
            }
        }

        // handle url as function
        if (typeof route.url === "function") {
            route.handler = route.url;
            delete route.url;
        }

        if (routeObj && typeof routeObj.handler === "function") {
            routeObj.handler = routeObj.handler;
        }

        return route;
    };

    /**
     * Checks if a route exists.
     *
     * @name exists
     * @function
     * @param {Object} req the request object
     * @return {Boolean} true, if the route was found, else false
     */
    self.exists = function (req) {
        return Boolean(self.getRoute(Url.parse(req.url).pathname));
    };

    /**
     * Reads the file and callbacks the content.
     *
     * @name readFile
     * @function
     * @param {String} file the relative path to the file
     * @param {Function} callback the callback function that will be called with an
     * err (first argument) and the content of the file (second argument)
     * @return {Buffer} the raw buffer
     */
    self.readFile = function (file, callback) {
        return Fs.readFile(self._root + file, function (err, buffer) {
            if (err) { return callback(err); }
            callback(null, buffer.toString())
        });
    };

    /**
     * Serves the HTML file by providing the @req and @res parameters
     *
     * @name serve
     * @function
     * @param {Object} req the request object
     * @param {Object} res the response object
     * @return {Object} the Statique instance
     */
    self.serve = function (req, res) {
        return self.serveRoute(undefined, req, res);
    };

    /**
     * This function is used for sending custom status messages and content
     * If the @content parameter is not provided or is not a string, the response
     * will not be ended. The status code and the headers will be set
     *
     * @name sendRes
     * @function
     * @param {Object} res the response object
     * @param {Number} statusCode the response status code
     * @param {String} mimeType the response mime type
     * @param {String} content the content that you want to send via response
     * @param {Object} otherHeaders Aditional headers that will be merged with
     * the basic ones. They have greater priority than basic headers.
     * @return {Object} the Statique instance
     */
    self.sendRes = function (res, statusCode, mimeType, content, otherHeaders) {

        var headers = {
            "Content-Type": mimeType || "plain/text"
          , "Server": "Statique Server"
        };

        for (var h in otherHeaders) {
            headers[h] = otherHeaders[h] === undefined ? headers[h] : otherHeaders[h];
        }

        res.writeHead(statusCode, headers);

        if (typeof content === "string" || content instanceof Buffer) {
            res.end(content);
        }

        return self;
    };

    /**
     * Serves a file
     *
     * @name serveFile
     * @function
     * @param {String|Object} path The path to the file that should be served or the
     * route object
     * @param {Number} statusCode The response status code (default: 200)
     * @param {Object} res The response object
     * @param {Object} req The request object
     * @param {Object} additionalHeaders Additional headers that should be sent
     * @param {String} customRoot Path to the custom root (e.g. "/")
     * @return {Object} Statique instance
     */
    self.serveFile = function (path, statusCode, res, req, additionalHeaders, customRoot) {

        res = Object(res);
        req = Object(req);
        req.headers = req.headers || {};

        if (typeof path === "string") {
            path = {
                reqUrl: path
            };
        }

        var fullPath = (customRoot ? customRoot : self._root) + (path.url || path.reqUrl);

        try {
            stats = Fs.lstatSync(fullPath);
        } catch (e) {
            self.error(req, res, 404, "Not found");
            return self;
        }

        // no file, no fun
        if (!stats.isFile()) { return self; }

        // cache stuff
        var mtime = Date.parse(stats.mtime)
          , clientETag = req.headers['if-none-match']
          , clientMTime = Date.parse(req.headers['if-modified-since'])
          , contentType = Mime.lookup(path.reqUrl)
          , headers = {
                "Etag": JSON.stringify([stats.ino, stats.size, mtime].join('-'))
              , "Date": (new Date()).toUTCString()
              , "Last-Modified": (new Date(stats.mtime)).toUTCString()
              , "Content-Type": contentType
              , "Content-Length": stats.size
            }
          ;

        // file is cached
        if ((clientMTime  || clientETag) &&
            (!clientETag  || clientETag === headers['Etag']) &&
            (!clientMTime || clientMTime >= mtime)) {
            // 304 response should not contain entity headers
            ['Content-Encoding',
             'Content-Language',
             'Content-Length',
             'Content-Location',
             'Content-MD5',
             'Content-Range',
             'Content-Type',
             'Expires',
             'Last-Modified'].forEach(function(entityHeader) {
                delete headers[entityHeader];
            });

            for (var aH in additionalHeaders) {
                headers[aH] = additionalHeaders[aH] || headers[aH];
            }

            self.sendRes(res, 304, contentType, null, headers);
            res.end();
            return self;
        }

        // Set cache-control  header
        headers["cache-control"] = "max-age=" + self._cache;
        for (var aH in additionalHeaders) {
            headers[aH] = additionalHeaders[aH] || headers[aH];
        }

        // file should cached
        self.sendRes(res, statusCode || 200, contentType, stats.size ? null : "", headers)

        if (stats.size) {
            var fileStream = Fs.createReadStream(fullPath);
            fileStream.pipe(res);
        }

        return self;
    };

    /**
     * Serves a provided route.
     *
     * @name serveRoute
     * @function
     * @param {String} route The route that should be served
     * @param {Object} req The request object
     * @param {Object} res The response object
     * @return {Object} The Statique instance
     */
    self.serveRoute = function (route, req, res) {

        var parsedUrl = Url.parse(req.url)
          , routeToServe = self.getRoute(
                route || parsedUrl.pathname
            ) || parsedUrl.pathname
          , stats = null
          , method = (req.method || "get").toLowerCase()
          , form = {
                data: ""
              , error: ""
              , _emitter: new Events.EventEmitter()
            }
          ;

        req.on("data", function (chunk) {
            form.data += chunk;
        });

        req.on("error", function (err) {
            form.error += err;
        });

        req.on("end", function () {
            form._emitter.emit("done", form);
        });

        var d = Domain.create();
        d.on("error", function(err) {
            self.emit("error", err);
            self.error(req, res, 500, "Internal Server Error");
            //Debug.log(e.stack, "error");
        });

        // Add request and response objects
        d.add(req);
        d.add(res);

        function foo(r, re) {
            setTimeout(function () {
                throw new Error("Hi");
            }, 1000);
        }

        debugger;
        // Run handlers
        if (routeToServe.url && typeof routeToServe.url[method] === "function") {
            d.run(function() {
                foo();
                //routeToServe.url[method](req, res, form._emitter);
            });
            return self;
        }

        if (typeof routeToServe.handler === "function") {
            d.run(function() {
                routeToServe.handler(req, res, form._emitter);
            });
            return self;
        }

        // Not a handler, try to serve the file
        return self.serveFile(routeToServe, null, res, req);
    };

    /**
     * Redirects the user to the new url passed in the second argument.
     *
     * @name redirect
     * @function
     * @param {String} res The response object
     * @param {String} newUrl The new url where the user should be redirected
     * @return {Object} Statique instance
     */
    self.redirect = function (res, newUrl) {
        return self.sendRes(res, 301, "text", "Redirecting", {
            "location": newUrl
        });
    };

    /**
     * Sends an error to client
     *
     * @name error
     * @function
     * @param {Object} req The request object
     * @param {Object} res The response object
     * @param {Number} errCode The error code
     * @param {String} errMessage The error message
     * @return {Object} Statique instance
     */
    self.error = function (req, res, errCode, errMessage) {
        var configErrors = Object(self._errors)
          , errPage = configErrors[errCode]
          ;

        if (errPage) {
            return self.serveFile(errPage, errCode, res, req, {
                "cache-control": "no-cache"
            });
        }

        return self.sendRes(res, errCode, "text", errMessage);
    };

    return self;
};
<|endoftext|>


<commit_before>var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var Facility = require('./facility.js');
var FieldSize = require('./fieldSize.js');

var Field = new Schema({
    name: {
        required: true,
        type: String
    },
    facility: {
        required: true,
        type: Schema.Types.ObjectId,
        ref: 'Facility'
    },
    size: {
        required: true,
        type: Schema.Types.ObjectId,
        ref: 'FieldSize'
    },
    lights: Boolean,
    condition: Number,
    game: Boolean,
    practice: Boolean,
    tournament: Boolean,
    training: Boolean,
    surface: {
        type: String,
        enum: ['GRASS', 'TURF']
    }
}, {
    timestamps: true
});

module.exports = mongoose.model('Field', Field);<commit_msg>removed restriction of facility being required<commit_after>var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var Facility = require('./facility.js');
var FieldSize = require('./fieldSize.js');

var Field = new Schema({
    name: {
        required: true,
        type: String
    },
    facility: {
        type: Schema.Types.ObjectId,
        ref: 'Facility'
    },
    size: {
        required: true,
        type: Schema.Types.ObjectId,
        ref: 'FieldSize'
    },
    lights: Boolean,
    condition: Number,
    game: Boolean,
    practice: Boolean,
    tournament: Boolean,
    training: Boolean,
    surface: {
        type: String,
        enum: ['GRASS', 'TURF']
    }
}, {
    timestamps: true
});

module.exports = mongoose.model('Field', Field);<|endoftext|>


<commit_before>var gulp    = require('gulp');
var shell   = require('gulp-shell');
var nodemon = require('gulp-nodemon')
var jshint  = require('gulp-jshint');

// gulp-inject packages
var inject = require("gulp-inject");
var mainBowerFiles = require('main-bower-files');
var angularFilesort = require('gulp-angular-filesort');
var print = require('gulp-print');

gulp.task('d', shell.task(['node-debug server/app/environments/development.js']));
gulp.task('default', shell.task(['node server/app/environments/development.js']));

function getScriptTagOrLinkTag(filepath) {
  if(filepath.indexOf('.css') === -1)
    return '<script src="'
           +filepath.replace('/client/public','')  +'"></script>';
  else
    return '<link rel="stylesheet" type="text/css" href="'
            +filepath.replace('/client/public/','')
            +'">';
}

gulp.task('inject', function () {
  var target = gulp.src('./client/public/index.html');

  var bower = gulp.src(mainBowerFiles({
      paths: {
          bowerDirectory: './client/public/bower_components',
          bowerrc: './.bowerrc',
          bowerJson: './bower.json'
      }
  }));

  target.pipe(inject(bower,
  {
    name: 'bower',
    transform: function (filepath, file, i, length) {
      return getScriptTagOrLinkTag(filepath);
    }
  }))
  .pipe(print())
  .pipe(gulp.dest('./client/public'));

  var sources = gulp.src(['./client/public/javascript/**/**/*.js', './client/public/css/**/*.css'], {read: false});

  target.pipe(inject(sources,
  {
    name: 'main',
    transform: function (filepath, file, i, length) {
      return getScriptTagOrLinkTag(filepath);
    }
  }))
  .pipe(gulp.dest('./client/public'));
});

//gulp.task('default', function(){
//    //nodemon({ script: 'server/app/environments/development.js', ext: 'html js', ignore: ['ignored.js'] })
//    //    .on('change', ['lint'])
//    //    .on('restart', function () {
//    //        console.log('restarted!')
//    //    });
//    shell.task([
//        'node server/app/environments/development.js'
//    ]);
//});
<commit_msg>gulp inject working<commit_after>var gulp    = require('gulp');
var shell   = require('gulp-shell');
var nodemon = require('gulp-nodemon')
var jshint  = require('gulp-jshint');

// gulp-inject packages
var inject = require("gulp-inject");
var mainBowerFiles = require('main-bower-files');
var angularFilesort = require('gulp-angular-filesort');
var print = require('gulp-print');

gulp.task('d', shell.task(['node-debug server/app/environments/development.js']));
gulp.task('default', shell.task(['node server/app/environments/development.js']));

function getScriptTagOrLinkTag(filepath) {
  if(filepath.indexOf('.css') === -1)
    return '<script src="'
           +filepath.replace('/client/public','')  +'"></script>';
  else
    return '<link rel="stylesheet" type="text/css" href="'
            +filepath.replace('/client/public/','')
            +'">';
}

gulp.task('inject', function () {
  var target = gulp.src('./client/public/index.html');

  var bower = gulp.src(mainBowerFiles({
      paths: {
          bowerDirectory: './client/public/bower',
          bowerrc: './.bowerrc',
          bowerJson: './bower.json'
      }
  }));

  target.pipe(inject(bower,
  {
    name: 'bower',
    transform: function (filepath, file, i, length) {
      return getScriptTagOrLinkTag(filepath);
    }
  }))
  .pipe(print())
  .pipe(gulp.dest('./client/public'));

  var sources = gulp.src(['./client/public/javascript/**/**/*.js', './client/public/css/**/*.css'], {read: false});

  target.pipe(inject(sources,
  {
    name: 'main',
    transform: function (filepath, file, i, length) {
      return getScriptTagOrLinkTag(filepath);
    }
  }))
  .pipe(gulp.dest('./client/public'));
});

//gulp.task('default', function(){
//    //nodemon({ script: 'server/app/environments/development.js', ext: 'html js', ignore: ['ignored.js'] })
//    //    .on('change', ['lint'])
//    //    .on('restart', function () {
//    //        console.log('restarted!')
//    //    });
//    shell.task([
//        'node server/app/environments/development.js'
//    ]);
//});
<|endoftext|>


<commit_before>var FS = require("fs");
var Request = require("ringo/webapp/request").Request;
var skinResponse = require("ringo/webapp/response").skinResponse;
var Map = require("geoscript/map").Map;

var map = new Map({
    layers: [{
        name: "states",
        title: "US States",
        workspace: FS.join(module.directory, "..", "data", "shapefiles"),
        style: {
            fillColor: "steelblue",
            strokeColor: "wheat"
        }
    }]
});

exports.index = function(req) {

    return skinResponse("skins/index.html", {
        host: req.host,
        port: req.port,
        layers: map.layers
    });

};

exports.wms = function(req) {

    var request = new Request(req);
    var bbox = request.queryParams["BBOX"].split(",").map(Number);

    var image = map.render({
        imageType: "png",
        bounds: bbox
    });

    return {
        status: 200,
        headers: {"Content-Type": "image/png"},
        body: [image]
    };

};
<commit_msg>Updates to work with latest Ringo.<commit_after>var FS = require("fs");
var Request = require("ringo/webapp/request").Request;
var Response = require("ringo/webapp/response").Response;
var Map = require("geoscript/map").Map;

var map = new Map({
    layers: [{
        name: "states",
        title: "US States",
        workspace: FS.join(module.directory, "..", "data", "shapefiles"),
        style: {
            fillColor: "steelblue",
            strokeColor: "wheat"
        }
    }]
});

exports.index = function(req) {

    return Response.skin(
        module.resolve("skins/index.html"),
        {host: req.host, port: req.port, layers: map.layers}
    );

};

exports.wms = function(req) {

    var request = new Request(req);
    var bbox = request.queryParams["BBOX"].split(",").map(Number);

    var image = map.render({
        imageType: "png",
        bounds: bbox
    });

    return {
        status: 200,
        headers: {"Content-Type": "image/png"},
        body: [image]
    };

};
<|endoftext|>


<commit_before>const request = require('request');
const config = require('../config');
const helper = require('./helper.js');

// Define the initial API
var api = {};
api.applications = {};
api.servers = {};

// Setup the URLs
api.applications.urls = {
  'all': 'https://api.newrelic.com/v2/applications.json',
  'one': 'https://api.newrelic.com/v2/applications/{id}.json',
  'metricNames': 'https://api.newrelic.com/v2/applications/{id}/metrics.json',
  'metricData': 'https://api.newrelic.com/v2/applications/{id}/metrics/data.json'
}

api.servers.urls = {
  'all': 'https://api.newrelic.com/v2/servers.json',
  'one': 'https://api.newrelic.com/v2/servers/{id}.json',
  'metricNames': 'https://api.newrelic.com/v2/servers/{id}/metrics.json',
  'metricData': 'https://api.newrelic.com/v2/servers/{id}/metrics/data.json'
}

// Applications List
api.applications.list = function applicationsList(cb) {
  helper.sendGetRequest(api.applications.urls.all, cb);
}

// Servers List
api.servers.list = function serversList(cb) {
  helper.sendGetRequest(api.servers.urls.all, cb);
}

// TODO: Users List

module.exports = api;
<commit_msg>adding more urls<commit_after>const request = require('request');
const config = require('../config');
const helper = require('./helper.js');

// Define the initial API
var api = {};
api.applications = {};
api.mobile = {};
api.servers = {};

// Nomenclature used:
// list - get all
// show - get one
// metricNames, metricData - only for one asset like {application_id}
api.applications.urls = {
  'list': 'https://api.newrelic.com/v2/applications.json',
  'show': 'https://api.newrelic.com/v2/applications/{application_id}.json',
  'metricNames': 'https://api.newrelic.com/v2/applications/{application_id}/metrics.json',
  'metricData': 'https://api.newrelic.com/v2/applications/{application_id}/metrics/data.json',
  'hosts': {
    'list': 'https://api.newrelic.com/v2/applications/{application_id}/hosts.json',
    'show': 'https://api.newrelic.com/v2/applications/{application_id}/hosts/{id}.json',
    'metricNames': 'https://api.newrelic.com/v2/applications/{application_id}/hosts/{host_id}/metrics.json',
    'metricData': 'https://api.newrelic.com/v2/applications/{application_id}/hosts/{host_id}/metrics/data.json'
  }
}

api.servers.urls = {
  'list': 'https://api.newrelic.com/v2/servers.json',
  'show': 'https://api.newrelic.com/v2/servers/{application_id}.json',
  'metricNames': 'https://api.newrelic.com/v2/servers/{application_id}/metrics.json',
  'metricData': 'https://api.newrelic.com/v2/servers/{application_id}/metrics/data.json'
}

// Applications List
api.applications.list = function applicationsList(cb) {
  helper.sendGetRequest(api.applications.urls.list, cb);
}

// Servers List
api.servers.list = function serversList(cb) {
  helper.sendGetRequest(api.servers.urls.list, cb);
}

// TODO: Users List

module.exports = api;
<|endoftext|>


<commit_before>/* eslint-disable */
var gulp = require('gulp'),
  path = require('path'),
  ngc = require('@angular/compiler-cli/src/main').main,
  rollup = require('gulp-rollup'),
  rename = require('gulp-rename'),
  del = require('del'),
  runSequence = require('run-sequence'),
  inlineResources = require('./gulp/inline-resources');

const rootFolder = path.join(__dirname);
const srcFolder = path.join(rootFolder, 'src');
const tmpFolder = path.join(rootFolder, '.tmp');
const buildFolder = path.join(rootFolder, 'build');
const distFolder = path.join(rootFolder, 'dist');
const tsconfig = "{{tsconfig}}"
const moduleId = "{{moduleId}}"
/**
 * 1. Delete /dist folder
 */
gulp.task('clean:dist', function () {

  // Delete contents but not dist folder to avoid broken npm links
  // when dist directory is removed while npm link references it.
  return deleteFolders([distFolder + '/**', '!' + distFolder, '!' + distFolder + "/index.js"]);
});

/**
 * 2. Clone the /src folder into /.tmp. If an npm link inside /src has been made,
 *    then it's likely that a node_modules folder exists. Ignore this folder
 *    when copying to /.tmp.
 */
gulp.task('copy:source', function () {
  return gulp.src([`${srcFolder}/**/*`, `!${srcFolder}/node_modules`])
    .pipe(gulp.dest(tmpFolder));
});

/**
 * 3. Inline template (.html) and style (.css) files into the the component .ts files.
 *    We do this on the /.tmp folder to avoid editing the original /src files
 */
gulp.task('inline-resources', function () {
  return Promise.resolve()
    .then(() => inlineResources(tmpFolder));
});

/**
 * 4. Run the Angular compiler, ngc, on the /.tmp folder. This will output all
 *    compiled modules to the /build folder.
 */
gulp.task('ngc', function () {
  return ngc({
    project: `${tmpFolder}/${tsconfig}`
  })
    .then((exitCode) => {
      if (exitCode === 1) {
        // This error is caught in the 'compile' task by the runSequence method callback
        // so that when ngc fails to compile, the whole compile process stops running
        throw new Error('ngc compilation failed');
      }
    });
});

/**
 * 5. Run rollup inside the /build folder to generate our Flat ES module and place the
 *    generated file into the /dist folder
 */
gulp.task('rollup:fesm', function () {
  return gulp.src(`${buildFolder}/**/*.js`)
  // transform the files here.
    .pipe(rollup({

      // Bundle's entry point
      // See https://github.com/rollup/rollup/wiki/JavaScript-API#entry
      entry: `${buildFolder}/index.js`,

      // Allow mixing of hypothetical and actual files. "Actual" files can be files
      // accessed by Rollup or produced by plugins further down the chain.
      // This prevents errors like: 'path/file' does not exist in the hypothetical file system
      // when subdirectories are used in the `src` directory.
      allowRealFiles: true,

      // A list of IDs of modules that should remain external to the bundle
      // See https://github.com/rollup/rollup/wiki/JavaScript-API#external
      external: [
        '@angular/core',
        '@angular/common'
      ],

      // Format of generated bundle
      // See https://github.com/rollup/rollup/wiki/JavaScript-API#format
      format: 'es'
    }))
    .pipe(gulp.dest(distFolder));
});

/**
 * 6. Run rollup inside the /build folder to generate our UMD module and place the
 *    generated file into the /dist folder
 */
gulp.task('rollup:umd', function () {
  return gulp.src(`${buildFolder}/**/*.js`)
  // transform the files here.
    .pipe(rollup({

      // Bundle's entry point
      // See https://github.com/rollup/rollup/wiki/JavaScript-API#entry
      entry: `${buildFolder}/index.js`,

      // Allow mixing of hypothetical and actual files. "Actual" files can be files
      // accessed by Rollup or produced by plugins further down the chain.
      // This prevents errors like: 'path/file' does not exist in the hypothetical file system
      // when subdirectories are used in the `src` directory.
      allowRealFiles: true,

      // A list of IDs of modules that should remain external to the bundle
      // See https://github.com/rollup/rollup/wiki/JavaScript-API#external
      external: [
        '@angular/core',
        '@angular/common'
      ],

      // Format of generated bundle
      // See https://github.com/rollup/rollup/wiki/JavaScript-API#format
      format: 'umd',

      // Export mode to use
      // See https://github.com/rollup/rollup/wiki/JavaScript-API#exports
      exports: 'named',

      // The name to use for the module for UMD/IIFE bundles
      // (required for bundles with exports)
      // See https://github.com/rollup/rollup/wiki/JavaScript-API#modulename
      moduleName: moduleId,

      // See https://github.com/rollup/rollup/wiki/JavaScript-API#globals
      globals: {
        typescript: 'ts'
      }

    }))
    .pipe(rename(moduleId + '.umd.js'))
    .pipe(gulp.dest(distFolder));
});

/**
 * 7. Copy all the files from /build to /dist, except .js files. We ignore all .js from /build
 *    because with don't need individual modules anymore, just the Flat ES module generated
 *    on step 5.
 */
gulp.task('copy:build', function () {
  return gulp.src([`${buildFolder}/**/*`, `!${buildFolder}/**/*.js`])
    .pipe(gulp.dest(distFolder));
});

/**
 * 8. Copy package.json from /src to /dist
 */
gulp.task('copy:manifest', function () {
  return gulp.src([`${srcFolder}/package.json`])
    .pipe(gulp.dest(distFolder));
});

/**
 * 9. Copy README.md from / to /dist
 */
gulp.task('copy:readme', function () {
  return gulp.src([path.join(rootFolder, 'README.MD')])
    .pipe(gulp.dest(distFolder));
});

/**
 * 10. Delete /.tmp folder
 */
gulp.task('clean:tmp', function () {
  return deleteFolders([tmpFolder]);
});

/**
 * 11. Delete /build folder
 */
gulp.task('clean:build', function () {
  return deleteFolders([buildFolder]);
});

gulp.task('compile', function () {
  runSequence(
    'clean:dist',
    'copy:source',
    'inline-resources',
    'ngc',
    'rollup:fesm',
    'rollup:umd',
    'copy:build',
    'copy:manifest',
    'copy:readme',
    'clean:build',
    'clean:tmp',
    function (err) {
      if (err) {
        console.log('ERROR:', err.message);
        deleteFolders([distFolder, tmpFolder, buildFolder]);
      } else {
        console.log('Compilation finished succesfully');
      }
    });
});

/**
 * Watch for any change in the /src folder and compile files
 */
gulp.task('watch', function () {
  gulp.watch(`${srcFolder}/**/*`, ['compile']);
});

gulp.task('clean', ['clean:dist', 'clean:tmp', 'clean:build']);

gulp.task('build', ['clean', 'compile']);
gulp.task('build:watch', ['build', 'watch']);
gulp.task('default', ['build:watch']);

/**
 * Deletes the specified folder
 */
function deleteFolders(folders) {
  return del(folders);
}
<commit_msg>added task to use specific tsconfig into the tmp folder<commit_after>/* eslint-disable */
var gulp = require('gulp'),
  path = require('path'),
  ngc = require('@angular/compiler-cli/src/main').main,
  rollup = require('gulp-rollup'),
  rename = require('gulp-rename'),
  del = require('del'),
  runSequence = require('run-sequence'),
  inlineResources = require('./gulp/inline-resources');

const rootFolder = path.join(__dirname);
const srcFolder = path.join(rootFolder, 'src');
const tmpFolder = path.join(rootFolder, '.tmp');
const buildFolder = path.join(rootFolder, 'build');
const distFolder = path.join(rootFolder, 'dist');
const tsconfigFilename = "tsconfig.build.json"
const tsconfigPath = path.join(rootFolder, 'gulp', tsconfigFilename)
const moduleName = "{{packageJSON.name}}"
/**
 * 1. Delete /dist folder
 */
gulp.task('clean:dist', function () {

  // Delete contents but not dist folder to avoid broken npm links
  // when dist directory is removed while npm link references it.
  return deleteFolders([distFolder + '/**', '!' + distFolder, '!' + distFolder + "/index.js"]);
});

/**
 * 2. Clone the /src folder into /.tmp. If an npm link inside /src has been made,
 *    then it's likely that a node_modules folder exists. Ignore this folder
 *    when copying to /.tmp.
 */
gulp.task('copy:source', function () {
  return gulp.src([`${srcFolder}/**/*`, `!${srcFolder}/node_modules`])
    .pipe(gulp.dest(tmpFolder));
});

/**
 * 3. Inline template (.html) and style (.css) files into the the component .ts files.
 *    We do this on the /.tmp folder to avoid editing the original /src files
 */
gulp.task('inline-resources', function () {
  return Promise.resolve()
    .then(() => inlineResources(tmpFolder));
});

gulp.task('copy:tsconfig', function () {
  return gulp.src([`${tsconfigPath}`])
    .pipe(gulp.dest(tmpFolder));
});
/**
 * 4. Run the Angular compiler, ngc, on the /.tmp folder. This will output all
 *    compiled modules to the /build folder.
 */
gulp.task('ngc', function () {
  return ngc({
    project: `${tmpFolder}/${tsconfigFilename}`
  })
    .then((exitCode) => {
      if (exitCode === 1) {
        // This error is caught in the 'compile' task by the runSequence method callback
        // so that when ngc fails to compile, the whole compile process stops running
        throw new Error('ngc compilation failed');
      }
    });
});

/**
 * 5. Run rollup inside the /build folder to generate our Flat ES module and place the
 *    generated file into the /dist folder
 */
gulp.task('rollup:fesm', function () {
  return gulp.src(`${buildFolder}/**/*.js`)
  // transform the files here.
    .pipe(rollup({

      // Bundle's entry point
      // See https://github.com/rollup/rollup/wiki/JavaScript-API#entry
      entry: `${buildFolder}/index.js`,

      // Allow mixing of hypothetical and actual files. "Actual" files can be files
      // accessed by Rollup or produced by plugins further down the chain.
      // This prevents errors like: 'path/file' does not exist in the hypothetical file system
      // when subdirectories are used in the `src` directory.
      allowRealFiles: true,

      // A list of IDs of modules that should remain external to the bundle
      // See https://github.com/rollup/rollup/wiki/JavaScript-API#external
      external: [
        '@angular/core',
        '@angular/common'
      ],

      // Format of generated bundle
      // See https://github.com/rollup/rollup/wiki/JavaScript-API#format
      format: 'es'
    }))
    .pipe(gulp.dest(distFolder));
});

/**
 * 6. Run rollup inside the /build folder to generate our UMD module and place the
 *    generated file into the /dist folder
 */
gulp.task('rollup:umd', function () {
  return gulp.src(`${buildFolder}/**/*.js`)
  // transform the files here.
    .pipe(rollup({

      // Bundle's entry point
      // See https://github.com/rollup/rollup/wiki/JavaScript-API#entry
      entry: `${buildFolder}/index.js`,

      // Allow mixing of hypothetical and actual files. "Actual" files can be files
      // accessed by Rollup or produced by plugins further down the chain.
      // This prevents errors like: 'path/file' does not exist in the hypothetical file system
      // when subdirectories are used in the `src` directory.
      allowRealFiles: true,

      // A list of IDs of modules that should remain external to the bundle
      // See https://github.com/rollup/rollup/wiki/JavaScript-API#external
      external: [
        '@angular/core',
        '@angular/common'
      ],

      // Format of generated bundle
      // See https://github.com/rollup/rollup/wiki/JavaScript-API#format
      format: 'umd',

      // Export mode to use
      // See https://github.com/rollup/rollup/wiki/JavaScript-API#exports
      exports: 'named',

      // The name to use for the module for UMD/IIFE bundles
      // (required for bundles with exports)
      // See https://github.com/rollup/rollup/wiki/JavaScript-API#modulename
      moduleName: moduleName,

      // See https://github.com/rollup/rollup/wiki/JavaScript-API#globals
      globals: {
        typescript: 'ts'
      }

    }))
    .pipe(rename(moduleName + '.umd.js'))
    .pipe(gulp.dest(distFolder));
});

/**
 * 7. Copy all the files from /build to /dist, except .js files. We ignore all .js from /build
 *    because with don't need individual modules anymore, just the Flat ES module generated
 *    on step 5.
 */
gulp.task('copy:build', function () {
  return gulp.src([`${buildFolder}/**/*`, `!${buildFolder}/**/*.js`])
    .pipe(gulp.dest(distFolder));
});

/**
 * 8. Copy package.json from /src to /dist
 */
gulp.task('copy:manifest', function () {
  return gulp.src([`${srcFolder}/package.json`])
    .pipe(gulp.dest(distFolder));
});

/**
 * 9. Copy README.md from / to /dist
 */
gulp.task('copy:readme', function () {
  return gulp.src([path.join(rootFolder, 'README.MD')])
    .pipe(gulp.dest(distFolder));
});

/**
 * 10. Delete /.tmp folder
 */
gulp.task('clean:tmp', function () {
  return deleteFolders([tmpFolder]);
});

/**
 * 11. Delete /build folder
 */
gulp.task('clean:build', function () {
  return deleteFolders([buildFolder]);
});

gulp.task('compile', function () {
  runSequence(
    'clean:dist',
    'copy:source',
    'copy:tsconfig',
    'inline-resources',
    'ngc',
    'rollup:fesm',
    'rollup:umd',
    'copy:build',
    'copy:manifest',
    'copy:readme',
    'clean:build',
    'clean:tmp',
    function (err) {
      if (err) {
        console.log('ERROR:', err.message);
        deleteFolders([distFolder, tmpFolder, buildFolder]);
      } else {
        console.log('Compilation finished succesfully');
      }
    });
});

/**
 * Watch for any change in the /src folder and compile files
 */
gulp.task('watch', function () {
  gulp.watch(`${srcFolder}/**/*`, ['compile']);
});

gulp.task('clean', ['clean:dist', 'clean:tmp', 'clean:build']);

gulp.task('build', ['clean', 'compile']);
gulp.task('build:watch', ['build', 'watch']);
gulp.task('default', ['build:watch']);

/**
 * Deletes the specified folder
 */
function deleteFolders(folders) {
  return del(folders);
}
<|endoftext|>