<commit_before>'use strict';
// var require = require('./lazyRequire')

// gulp stuff
var gulp = require('gulp');
var uglify = require('gulp-uglify');
var wrap = require("gulp-wrap");
var concat = require("gulp-concat");
var streamify = require('gulp-streamify');
var flatten = require('gulp-flatten');
var angularTemplates = require('gulp-angular-templatecache')
var htmlmin = require('gulp-htmlmin');
var notify = require("gulp-notify");

// for backend unit tests
var jasmineReporter = require('./backend/jasmineReporter')
var jasmine = require('gulp-jasmine');

// browsify stuff
var browserify = require('browserify');
var source = require('vinyl-source-stream');
var watchify = require('watchify')
var glob = require('glob')
var karma = require('karma')

//other stuff
var _ = require('lodash')
var path = require('path')

// custom stuff
// var macros = require('./backend/macros')
// var pointer = require('./backend/pointer');
// var emailMgr = require('./backend/emailMgr')
// var pageDataMgr =
// var search = require('./backend/search')

// // parsers and databases
// var requireDir = require('require-dir');
// var parsers = requireDir('./backend/parsers');
// var databases = requireDir('./backend/databases');

//this is not used atm
gulp.task('uglifyCSS', function () {
	return gulp.src('frontend/css/homepage/*.css')
		// .pipe(concat('allthecss.css'))
		.pipe(uncss({
			html: ['frontend/static/index.html']
		}))
		// .pipe(addsrc('frontend/css/all/*.css'))
		.pipe(concat('allthecss.css'))
		.pipe(gulp.dest('frontend/static/css'));
});

gulp.task('watchUglifyCSS', function () {
	gulp.watch(['frontend/css/*.css'], ['uglifyCSS']);
});

function onError(err) {
	// print the error (can replace with gulp-util)
	console.log(err.message);
	notify.onError({
		message: 'Error: <%= error.message %>',
		sound: false // deactivate sound?
	})(err);
}

//javascript

//watch is allways on, to turn off (or add the option back)
// turn fullPaths back to shouldWatch and only run bundler = watchify(bundler) is shouldWatch is true
// http://blog.avisi.nl/2014/04/25/how-to-keep-a-fast-build-with-browserify-and-reactjs/

//another note, if you include a module that dosent exist, it will silently hang forever(?) eg (require('jdklfjdasjfkl'))
function compileJS(shouldUglify) {
	var files = glob.sync('frontend/src/**/*.js');

	var filesToProccess = [];
	files.forEach(function (file) {
		if (!_(file).includes('tests')) {
			filesToProccess.push(file)
		};
	})

	console.log('Processing:', filesToProccess)

	var bundler = browserify({
		entries: filesToProccess
	}, {
		basedir: __dirname,
		debug: false,
		cache: {}, // required for watchify
		packageCache: {}, // required for watchify

		// required to be true only for watchify (?) but watchify works when it is off.
		//dont show C:/ryan/google drive etc in the uglified source code
		fullPaths: false
	});

	if (shouldUglify) {
		bundler.ignore('./testsMgr');
		bundler.ignore('./tests/testsMgr');
		bundler.ignore('../tests/testsMgr');
	}

	bundler = watchify(bundler)

	var rebundle = function () {
		console.log("----Rebundling custom JS!----")
		var stream = bundler.bundle();

		stream.on('error', function (err) {
			onError(err);
			// end this stream
			this.emit('end');
		})

		stream = stream.pipe(source('allthejavascript.js'));

		if (shouldUglify) {
			stream = stream.pipe(streamify(uglify({
				options: {
					ie_proof: false
				},
				compress: {
					drop_console: true,
					unsafe: true,
					collapse_vars: true,
					pure_getters: true,
					// warnings: true,
					// keep_fnames: true
				}
			})));
		}

		stream = stream.pipe(gulp.dest('./frontend/static/js/internal'));

		stream.on('end', function () {
			console.log("----Done Rebundling custom JS!----")
		}.bind(this))

		return stream;
	};

	bundler.on('update', rebundle);
	return rebundle();
}

gulp.task('copyHTML', function () {
	return gulp
		.src('./frontend/src/**/*.html')
		.pipe(flatten())
		// .pipe(rename({
		// 	dirname:'html'
		// }))
		.pipe(htmlmin({
			collapseWhitespace: true,
			removeComments: true
		}))
		.pipe(angularTemplates({
			module: 'templates',
			standalone: true
		}))
		.pipe(concat('html.js'))
		.pipe(gulp.dest('./frontend/static/js/internal'))
})

gulp.task('watchCopyHTML', function () {
	gulp.watch(['frontend/src/**/*.html'], ['copyHTML']);
});

//production
gulp.task('uglifyJS', function () {
	return compileJS(true);
});

//main prod starting point
gulp.task('prod', ['uglifyJS', 'watchCopyHTML', 'copyHTML'], function () {
	require('./backend/server')
})

//development
gulp.task('compressJS', function () {
	return compileJS(false);
});

gulp.task('dev', ['compressJS', 'watchCopyHTML', 'copyHTML'], function () {
	require('./backend/server')
})

//other

// when frontend tests work, add them here
// gulp.task('tests', function () {

// 	//run all of the parser tests
// 	for (var parserName in parsers) {
// 		parsers[parserName].tests();
// 	}

// 	//run all of the db tests
// 	for (var databaseName in databases) {
// 		databases[databaseName].tests();
// 	}

// 	pointer.tests();
// 	emailMgr.tests();
// 	pageDataMgr.tests();
// 	search.tests();
// });

gulp.task('ftest', ['watchCopyHTML', 'copyHTML'], function () {
	new karma.Server({
		configFile: __dirname + '/frontend/karma.conf.js',
	}, function (exitCode) {
		console.log('ERROR Karma has exited with ' + exitCode)
		process.exit()
	}.bind(this)).start();
});

// if u want to u can run individual test files with
// jasmine-node ellucianSectionParser.tests.js  --matchall
gulp.task('btestRun', function () {
	var files = glob.sync('backend/**/*.js');

	files.forEach(function (file) {
		var filePath = path.resolve(file);
		delete require.cache[filePath]
	}.bind(this))

	// gulp-jasmine works on filepaths so you can't have any plugins before it
	gulp.src('backend/**/*.tests.js')

	.pipe(jasmine({
		reporter: new jasmineReporter()
	}))
	.on('error', function (err) {
		onError(err);
		this.emit('end');
	})
});

gulp.task('btest', ['btestRun'], function () {
	gulp.watch(['backend/**/*.js'], ['btestRun']);
});

gulp.task('spider', function () {
	require('./backend/pageDataMgr').main()
});
<commit_msg>on stream error gulp-nofiy<commit_after>'use strict';
// var require = require('./lazyRequire')

// gulp stuff
var gulp = require('gulp');
var uglify = require('gulp-uglify');
var wrap = require("gulp-wrap");
var concat = require("gulp-concat");
var streamify = require('gulp-streamify');
var flatten = require('gulp-flatten');
var angularTemplates = require('gulp-angular-templatecache')
var htmlmin = require('gulp-htmlmin');
var notify = require("gulp-notify");

// for backend unit tests
var jasmineReporter = require('./backend/jasmineReporter')
var jasmine = require('gulp-jasmine');

// browsify stuff
var browserify = require('browserify');
var source = require('vinyl-source-stream');
var watchify = require('watchify')
var glob = require('glob')
var karma = require('karma')

//other stuff
var _ = require('lodash')
var path = require('path')

// custom stuff
// var macros = require('./backend/macros')
// var pointer = require('./backend/pointer');
// var emailMgr = require('./backend/emailMgr')
// var pageDataMgr =
// var search = require('./backend/search')

// // parsers and databases
// var requireDir = require('require-dir');
// var parsers = requireDir('./backend/parsers');
// var databases = requireDir('./backend/databases');

//this is not used atm
gulp.task('uglifyCSS', function () {
	return gulp.src('frontend/css/homepage/*.css')
		// .pipe(concat('allthecss.css'))
		.pipe(uncss({
			html: ['frontend/static/index.html']
		}))
		// .pipe(addsrc('frontend/css/all/*.css'))
		.pipe(concat('allthecss.css'))
		.pipe(gulp.dest('frontend/static/css'));
});

gulp.task('watchUglifyCSS', function () {
	gulp.watch(['frontend/css/*.css'], ['uglifyCSS']);
});

function onError(err) {

	if (!err.message) {
		err = {
			message:err
		}
	}

	// print the error (can replace with gulp-util)
	console.log(err.message);
	notify.onError({
		message: 'Error: <%= error.message %>',
		sound: false // deactivate sound?
	})(err);
}

//javascript

//watch is allways on, to turn off (or add the option back)
// turn fullPaths back to shouldWatch and only run bundler = watchify(bundler) is shouldWatch is true
// http://blog.avisi.nl/2014/04/25/how-to-keep-a-fast-build-with-browserify-and-reactjs/

//another note, if you include a module that dosent exist, it will silently hang forever(?) eg (require('jdklfjdasjfkl'))
function compileJS(shouldUglify) {
	var files = glob.sync('frontend/src/**/*.js');

	var filesToProccess = [];
	files.forEach(function (file) {
		if (!_(file).includes('tests')) {
			filesToProccess.push(file)
		};
	})

	console.log('Processing:', filesToProccess)

	var bundler = browserify({
		entries: filesToProccess
	}, {
		basedir: __dirname,
		debug: false,
		cache: {}, // required for watchify
		packageCache: {}, // required for watchify

		// required to be true only for watchify (?) but watchify works when it is off.
		//dont show C:/ryan/google drive etc in the uglified source code
		fullPaths: false
	});

	if (shouldUglify) {
		bundler.ignore('./testsMgr');
		bundler.ignore('./tests/testsMgr');
		bundler.ignore('../tests/testsMgr');
	}

	bundler = watchify(bundler)

	var rebundle = function () {
		console.log("----Rebundling custom JS!----")
		var stream = bundler.bundle();

		stream.on('error', function (err) {
			onError(err);
			// end this stream
			this.emit('end');
		})

		stream = stream.pipe(source('allthejavascript.js'));

		if (shouldUglify) {
			stream = stream.pipe(streamify(uglify({
				options: {
					ie_proof: false
				},
				compress: {
					drop_console: true,
					unsafe: true,
					collapse_vars: true,
					pure_getters: true,
					// warnings: true,
					// keep_fnames: true
				}
			})));
		}

		stream = stream.pipe(gulp.dest('./frontend/static/js/internal'));

		stream.on('end', function () {
			console.log("----Done Rebundling custom JS!----")
		}.bind(this))

		return stream;
	};

	bundler.on('update', rebundle);
	return rebundle();
}

gulp.task('copyHTML', function () {
	return gulp
		.src('./frontend/src/**/*.html')
		.pipe(flatten())
		// .pipe(rename({
		// 	dirname:'html'
		// }))
		.pipe(htmlmin({
			collapseWhitespace: true,
			removeComments: true
		}))
		.pipe(angularTemplates({
			module: 'templates',
			standalone: true
		}))
		.pipe(concat('html.js'))
		.pipe(gulp.dest('./frontend/static/js/internal'))
})

gulp.task('watchCopyHTML', function () {
	gulp.watch(['frontend/src/**/*.html'], ['copyHTML']);
});

//production
gulp.task('uglifyJS', function () {
	return compileJS(true);
});

//main prod starting point
gulp.task('prod', ['uglifyJS', 'watchCopyHTML', 'copyHTML'], function () {
	require('./backend/server')
})

//development
gulp.task('compressJS', function () {
	return compileJS(false);
});

gulp.task('dev', ['compressJS', 'watchCopyHTML', 'copyHTML'], function () {
	require('./backend/server')
})

//other

// when frontend tests work, add them here
// gulp.task('tests', function () {

// 	//run all of the parser tests
// 	for (var parserName in parsers) {
// 		parsers[parserName].tests();
// 	}

// 	//run all of the db tests
// 	for (var databaseName in databases) {
// 		databases[databaseName].tests();
// 	}

// 	pointer.tests();
// 	emailMgr.tests();
// 	pageDataMgr.tests();
// 	search.tests();
// });

gulp.task('ftest', ['watchCopyHTML', 'copyHTML'], function () {
	new karma.Server({
		configFile: __dirname + '/frontend/karma.conf.js',
	}, function (exitCode) {
		console.log('ERROR Karma has exited with ' + exitCode)
		onError('KARMA has crashed!!!!');
		process.exit()
	}.bind(this)).start();
});

// if u want to u can run individual test files with
// jasmine-node ellucianSectionParser.tests.js  --matchall
gulp.task('btestRun', function () {
	var files = glob.sync('backend/**/*.js');

	files.forEach(function (file) {
		var filePath = path.resolve(file);
		delete require.cache[filePath]
	}.bind(this))

	// gulp-jasmine works on filepaths so you can't have any plugins before it
	gulp.src('backend/**/*.tests.js')

	.pipe(jasmine({
		reporter: new jasmineReporter()
	}))
	.on('error', function (err) {
		onError(err);
		this.emit('end');
	})
});

gulp.task('btest', ['btestRun'], function () {
	gulp.watch(['backend/**/*.js'], ['btestRun']);
});

gulp.task('spider', function () {
	require('./backend/pageDataMgr').main()
});
<|endoftext|>


<commit_before>// Introduction:
// -------------
// This is used to crawl swiftdoc.org to create a Fathead output
// that's specified in http://docs.duckduckhack.com/resources/fathead-overview.html
//
// Page Structure:
// ---------------
// Crawling swiftdoc.org is simple. All the links can be found in the root domain (index.html)
// so all we have to do is to collect all those links and send them off to processing.
//
// Pipeline:
// ---------
// Fetch -> Scrape / Process -> Output
//
// TODO:
// - Create disambiguations
//
// Updates:
// --------
// July 27, 2016: Initial version. Working with Swift 2.2. Articles and redirects working.

const req = require('request');
const cheerio = require('cheerio');
const sanitizeHtml = require('sanitize-html');
const fs = require('fs');

// This is the Swift version number that we'll be parsing.
const version = "v2.2";
const domain = "http://swiftdoc.org";
const rootURL = `${domain}/${version}/`;

// These are the selectors that we'll be using to scrape the needed data.
const selectors = {
  indexLinks: ".container-fluid.home-block a",
  codeSnippet: ".intro-declaration",
  description: ".discussion.comment",
  func: "div[id*='func-'].declaration",
  propDescription: ".comment",
  instanceVars: "div[id*='static-'].declaration",
  vars: "div[id*='var-'].declaration"
}

// Find all the articles.
//
// The homepage contains all of the links to each of the articles.
// However, not all links on index.html link to an article so we
// specify `.container-fluid.home-block a` to get a list of all
// the links to the article.
const fetch = (callback) => {
  request(rootURL, (body) => {
      const $ = cheerio.load(body);

      // Get all the links on the front-page then pass
      // the links that we get to the callback function for scraping.
      let links = [];
      $(selectors.indexLinks).each((_, el) => {
        let link = $(el).attr("href");
        links.push(link);
      });

      callback(links);
  });
};

// Scrape each article to get the data.
//
// Most articles have a snippet of code and a comment (specifically the types and protocols).
// We can easily select those sections of the page and parse out the unnecessary
// tags. We only keep <pre> and <code> tags.
fetch(links => {
  // We make a request for each link that we have so that
  // we can scrape each one.
  const scrape = link => {
    request(`${domain}${link}`, (body) => {

      // We create a Fathead object which contains all of the articles
      // and redirects that we have in the page. We will later process this to create the
      // output file.
      let Fathead = {
        articles: [],
        redirects: [],
      }

      const $ = cheerio.load(body);

      // ===================================================================
      // Scraping: First Pass 🔨
      // ===================================================================
      //
      // We go through the all the links that we got and we create entries for each
      // of them (provided they have any content in them that we can use).
      let article = {
        title: $("header").text().trim(),
        url: `${domain}${link}`,
        description: ''
      };

      // Sections "Protocols" and "Types" would usually fall into this block.
      [selectors.codeSnippet, selectors.description].forEach((selector, index) => {
        let html = sanitize($(selector)) || '';
        // The first entry is always the code snippet. We wrap that in a <pre> tag.
        if(index === 0 && html) { html = preWrap(html); }
        article.description += html;
      });

      // Sections "Operators" and "Globals" usually fall in here since most of them
      // lack a description.
      if(!article.description) {
        let description = sanitize($("code").eq(0));
        if(/operator/.test(description)) {
          article.description = preWrap(`<code>${description}</code>`);
        }
      }

      // Don't save it if it really doesn't have a description.
      if(article.description) {
        Fathead.articles.push(article);
      }

      // ===================================================================
      // Scraping: Second Pass ⚒
      // ===================================================================
      //
      // Now each of the pages can have functions and variables. These can
      // be made into individual articles as well. For example, searching for
      // "swift forEach" should return "SequenceType.forEach" as the article.
      let options = {
        parentArticle: article.title,
        $: $,
        link: link,
      };

      // Get the functions
      [options.selector, options.re] = [selectors.func, /func ([a-zA-Z]+)/];
      Fathead.articles = Fathead.articles.concat(findProperties(options));

      // Get the static variables
      [options.selector, options.re] = [selectors.instanceVars, /static var ([a-zA-Z]+)/];
      Fathead.articles = Fathead.articles.concat(findProperties(options));

      // Get the variables
      [options.selector, options.re] = [selectors.vars, /var ([a-zA-Z]+)/];
      Fathead.articles = Fathead.articles.concat(findProperties(options));

      // ===================================================================
      // Processing: Making Redirects ⚙
      // ===================================================================
      //
      // Finally let's make redirects. For example, "forEach" should redirect to "SequenceType.forEach"
      Fathead.articles.forEach(a => {
        if(/\./.test(a.title)) {
          let prop = a.title.match(/\.([a-zA-Z]+)$/);
          if(prop && prop.length > 1) {
            Fathead.redirects.push({
              title: prop[1],
              redirect: a.title,
            });
          }
        }
      });

      // ===================================================================
      // Processing: Making Disambiguations (TODO) ⚙
      // ===================================================================
      //
      // ???

      // ===================================================================
      // Write the file 📜
      // ===================================================================
      //
      // Finally, we write the contents to output.txt
      Fathead.articles.forEach(a => {
        fs.appendFileSync("output.txt", `${a.title}\tA\t\t\t\t\t\t\t\t\t\t${a.description}\t${a.url}\n`);
      });

      Fathead.redirects.forEach(r => {
        fs.appendFileSync("output.txt", `${r.title}\tR\t${r.redirect}\t\t\t\t\t\t\t\t\t\t\n`);
      });
    });
  }

  links.forEach(scrape);
});

// ===================================================================
// Utility Functions 🔧
// ===================================================================
//
// We declare these with the old function declaration so that they get hoisted up.

// This removes unnecessary tags.
// The function removes all the other tags in the HTML
// except for the <code> and <pre> tags. This also escapes newlines.
function sanitize(html) {
  return sanitizeHtml(html.html() || '', {
    allowedTags: [ 'code', 'pre', 'strong' ],
    allowedAttributes: {}
  }).trim().replace(/\n+/g, "\\n");
};

// This is simply a helper function which wraps HTML in a <pre> tag.
function preWrap(html) {
  return `<pre>${html}</pre>`;
}

// This is an abstraction to the `request` module.
// All we need to know is if we got it successfully or not.
function request(url, success) {
  req(url, (_, resp, body) => {
    if(resp.statusCode === 200) {
      success(body);
    } else {
      console.log(`Failed to load ${url}. Exiting.`);
      process.exit(1);
    }
  });
}

// This is a function for abstracting away the details that we need to
// get all the functions and variable declarations on the page that we're scraping.
function findProperties(options) {
  let articleList = [];

  options.$(options.selector).each((_, el) => {
    let strippedName = options.$(".toggle-link", el).text().match(options.re);
    let id = options.$(el).attr("id");

    // We make sure that we found a match with our regexp.
    if(strippedName && strippedName.length > 1) {
      let article = {
        title: `${options.parentArticle}.${strippedName[1]}`,
        url: `${domain}${options.link}#${id}`,
        description: sanitize(options.$(selectors.propDescription, el)),
      };

      // We don't want to duplicate entries that have already been decleared
      // somewhere else.
      if(!/Declared In/.test(article.description)) {
        articleList.push(article);
      }
    }
  });

  return articleList;
}
<commit_msg>Switched to new fathead template.<commit_after>// Introduction:
// -------------
// This is used to crawl swiftdoc.org to create a Fathead output
// that's specified in http://docs.duckduckhack.com/resources/fathead-overview.html
//
// Page Structure:
// ---------------
// Crawling swiftdoc.org is simple. All the links can be found in the root domain (index.html)
// so all we have to do is to collect all those links and send them off to processing.
//
// Pipeline:
// ---------
// Fetch -> Scrape / Process -> Output
//
// TODO:
// - Create disambiguations
//
// Updates:
// --------
// July 27, 2016: Initial version. Working with Swift 2.2. Articles and redirects working.

const req = require('request');
const cheerio = require('cheerio');
const sanitizeHtml = require('sanitize-html');
const fs = require('fs');

// This is the Swift version number that we'll be parsing.
const version = "v2.2";
const domain = "http://swiftdoc.org";
const rootURL = `${domain}/${version}/`;

// These are the selectors that we'll be using to scrape the needed data.
const selectors = {
  indexLinks: ".container-fluid.home-block a",
  codeSnippet: ".intro-declaration",
  description: ".discussion.comment",
  func: "div[id*='func-'].declaration",
  propDescription: ".comment",
  instanceVars: "div[id*='static-'].declaration",
  vars: "div[id*='var-'].declaration"
}

// Find all the articles.
//
// The homepage contains all of the links to each of the articles.
// However, not all links on index.html link to an article so we
// specify `.container-fluid.home-block a` to get a list of all
// the links to the article.
const fetch = (callback) => {
  request(rootURL, (body) => {
      const $ = cheerio.load(body);

      // Get all the links on the front-page then pass
      // the links that we get to the callback function for scraping.
      let links = [];
      $(selectors.indexLinks).each((_, el) => {
        let link = $(el).attr("href");
        links.push(link);
      });

      callback(links);
  });
};

// Scrape each article to get the data.
//
// Most articles have a snippet of code and a comment (specifically the types and protocols).
// We can easily select those sections of the page and parse out the unnecessary
// tags. We only keep <pre> and <code> tags.
fetch(links => {
  // We make a request for each link that we have so that
  // we can scrape each one.
  const scrape = link => {
    request(`${domain}${link}`, (body) => {

      // We create a Fathead object which contains all of the articles
      // and redirects that we have in the page. We will later process this to create the
      // output file.
      let Fathead = {
        articles: [],
        redirects: [],
      }

      const $ = cheerio.load(body);

      // ===================================================================
      // Scraping: First Pass 🔨
      // ===================================================================
      //
      // We go through the all the links that we got and we create entries for each
      // of them (provided they have any content in them that we can use).
      let article = {
        title: $("header").text().trim(),
        url: `${domain}${link}`,
        description: ''
      };

      // Sections "Protocols" and "Types" would usually fall into this block.
      [selectors.codeSnippet, selectors.description].forEach((selector, index) => {
        let html = sanitize($(selector)) || '';
        // The first entry is always the code snippet. We wrap that in a <pre> tag.
        if(index === 0 && html) { html = preWrap(html); }
        if(index === 1 && html) { html = '<p>' + html + '</p>'}
        article.description += html;
      });

      // Sections "Operators" and "Globals" usually fall in here since most of them
      // lack a description.
      if(!article.description) {
        let description = sanitize($("code").eq(0));
        if(/operator/.test(description)) {
          article.description = preWrap(`<code>${description}</code>`);
        }
      }

      // Don't save it if it really doesn't have a description.
      if(article.description) {
        Fathead.articles.push(article);
      }

      // ===================================================================
      // Scraping: Second Pass ⚒
      // ===================================================================
      //
      // Now each of the pages can have functions and variables. These can
      // be made into individual articles as well. For example, searching for
      // "swift forEach" should return "SequenceType.forEach" as the article.
      let options = {
        parentArticle: article.title,
        $: $,
        link: link,
      };

      // Get the functions
      [options.selector, options.re] = [selectors.func, /func ([a-zA-Z]+)/];
      Fathead.articles = Fathead.articles.concat(findProperties(options));

      // Get the static variables
      [options.selector, options.re] = [selectors.instanceVars, /static var ([a-zA-Z]+)/];
      Fathead.articles = Fathead.articles.concat(findProperties(options));

      // Get the variables
      [options.selector, options.re] = [selectors.vars, /var ([a-zA-Z]+)/];
      Fathead.articles = Fathead.articles.concat(findProperties(options));

      // ===================================================================
      // Processing: Making Redirects ⚙
      // ===================================================================
      //
      // Finally let's make redirects. For example, "forEach" should redirect to "SequenceType.forEach"
      Fathead.articles.forEach(a => {
        if(/\./.test(a.title)) {
          let prop = a.title.match(/\.([a-zA-Z]+)$/);
          if(prop && prop.length > 1) {
            Fathead.redirects.push({
              title: prop[1],
              redirect: a.title,
            });
          }
        }
      });

      // ===================================================================
      // Processing: Making Disambiguations (TODO) ⚙
      // ===================================================================
      //
      // ???

      // ===================================================================
      // Write the file 📜
      // ===================================================================
      //
      // Finally, we write the contents to output.txt
      Fathead.articles.forEach(a => {
        fs.appendFileSync("output.txt", `${a.title}\tA\t\t\t\t\t\t\t\t\t\t<section class="prog__container">${a.description}</section>\t${a.url}\n`);
      });

      Fathead.redirects.forEach(r => {
        fs.appendFileSync("output.txt", `${r.title}\tR\t${r.redirect}\t\t\t\t\t\t\t\t\t\t\n`);
      });
    });
  }

  links.forEach(scrape);
});

// ===================================================================
// Utility Functions 🔧
// ===================================================================
//
// We declare these with the old function declaration so that they get hoisted up.

// This removes unnecessary tags.
// The function removes all the other tags in the HTML
// except for the <code> and <pre> tags. This also escapes newlines.
function sanitize(html) {
  return sanitizeHtml(html.html() || '', {
    allowedTags: [ 'code', 'pre', 'strong' ],
    allowedAttributes: {}
  }).trim().replace(/\n+/g, "\\n");
};

// This is simply a helper function which wraps HTML in a <pre> tag.
function preWrap(html) {
  return `<pre>${html}</pre>`;
}

// This is an abstraction to the `request` module.
// All we need to know is if we got it successfully or not.
function request(url, success) {
  req(url, (_, resp, body) => {
    if(resp.statusCode === 200) {
      success(body);
    } else {
      console.log(`Failed to load ${url}. Exiting.`);
      process.exit(1);
    }
  });
}

// This is a function for abstracting away the details that we need to
// get all the functions and variable declarations on the page that we're scraping.
function findProperties(options) {
  let articleList = [];

  options.$(options.selector).each((_, el) => {
    let strippedName = options.$(".toggle-link", el).text().match(options.re);
    let id = options.$(el).attr("id");

    // We make sure that we found a match with our regexp.
    if(strippedName && strippedName.length > 1) {
      let article = {
        title: `${options.parentArticle}.${strippedName[1]}`,
        url: `${domain}${options.link}#${id}`,
        description: sanitize(options.$(selectors.propDescription, el)),
      };

      // We don't want to duplicate entries that have already been decleared
      // somewhere else.
      if(!/Declared In/.test(article.description)) {
        articleList.push(article);
      }
    }
  });

  return articleList;
}
<|endoftext|>


<commit_before>'use strict';

var React = require('react');
var ReactDOM = require('react-dom');
var QuillMixin = require('./mixin');
var find = require('lodash/find');
var some = require('lodash/some');
var isEqual = require('lodash/isEqual');
var T = React.PropTypes;

var QuillComponent = React.createClass({

	displayName: 'Quill',

	mixins: [ QuillMixin ],

	propTypes: {
		id: T.string,
		className: T.string,
		theme: T.string,
		style: T.object,
		readOnly: T.bool,
		value: T.string,
		defaultValue: T.string,
		placeholder: T.string,
		bounds: T.oneOfType([T.string, T.element]),
		onKeyPress: T.func,
		onKeyDown: T.func,
		onKeyUp: T.func,
		onChange: T.func,
		onChangeSelection: T.func,

		modules: function(props) {
			var isNotObject = T.object.apply(this, arguments);
			if (isNotObject) return isNotObject;

			if (
				props.modules &&
				props.modules.toolbar &&
				props.modules.toolbar[0] &&
				props.modules.toolbar[0].type
			) return new Error(
				'Since v1.0.0, React Quill will not create a custom toolbar for you ' +
				'anymore. Create a toolbar explictly, or let Quill create one. ' +
				'See: https://github.com/zenoamaro/react-quill#upgrading-to-react-quill-v1-0-0'
			);
		},

		toolbar: function(props) {
			if ('toolbar' in props) return new Error(
				'The `toolbar` prop has been deprecated. Use `modules.toolbar` instead. ' +
				'See: https://github.com/zenoamaro/react-quill#upgrading-to-react-quill-v1-0-0'
			);
		},

		formats: function(props) {
			var isNotArrayOfString = T.arrayOf(T.string).apply(this, arguments);

			if (isNotArrayOfString) return new Error(
				'You cannot specify custom `formats` anymore. Use Parchment instead.  ' +
				'See: https://github.com/zenoamaro/react-quill#upgrading-to-react-quill-v1-0-0'
			);
		},

		styles: function(props) {
			if ('styles' in props) return new Error(
				'The `styles` prop has been deprecated. Use custom stylesheets instead. ' +
				'See: https://github.com/zenoamaro/react-quill#upgrading-to-react-quill-v1-0-0'
			);
		},

		pollInterval: function(props) {
			if ('pollInterval' in props) return new Error(
				'The `pollInterval` property does not have any effect anymore. ' +
				'You can safely remove it from your props.' +
				'See: https://github.com/zenoamaro/react-quill#upgrading-to-react-quill-v1-0-0'
			);
		}
	},

	/*
	Changing one of these props should cause a re-render.
	*/
	dirtyProps: [
		'id',
		'className',
		'style',
		'modules',
		'formats',
		'bounds',
		'theme',
	],

	getDefaultProps: function() {
		return {
			className: '',
			theme: 'snow',
			modules: {},
		};
	},

	/*
	We consider the component to be controlled if
	whenever `value` is being sent in props.
	*/
	isControlled: function() {
		return 'value' in this.props;
	},

	getInitialState: function() {
		return {
			value: this.isControlled()
				? this.props.value
				: this.props.defaultValue
		};
	},

	componentWillReceiveProps: function(nextProps) {
		var editor = this.editor;
		// If the component is unmounted and mounted too quickly
		// an error is thrown in setEditorContents since editor is
		// still undefined. Must check if editor is undefined
		// before performing this call.
		if (editor) {
			// Update only if we've been passed a new `value`.
			// This leaves components using `defaultValue` alone.
			if ('value' in nextProps) {
				// NOTE: Seeing that Quill is missing a way to prevent
				//       edits, we have to settle for a hybrid between
				//       controlled and uncontrolled mode. We can't prevent
				//       the change, but we'll still override content
				//       whenever `value` differs from current state.
				if (nextProps.value !== this.getEditorContents()) {
					this.setEditorContents(editor, nextProps.value);
				}
			}
			// We can update readOnly state in-place.
			if ('readOnly' in nextProps) {
				if (nextProps.readOnly !== this.props.readOnly) {
					this.setEditorReadOnly(editor, nextProps.readOnly);
				}
			}
		}
	},

	componentDidMount: function() {
		var editor = this.createEditor(
			this.getEditorElement(),
			this.getEditorConfig()
		);
		this.editor = editor;
	},

	componentWillUnmount: function() {
		// NOTE: Don't set the state to null here
		//       as it would generate a loop.
		var e = this.getEditor();
		if (e) this.unhookEditor(e);
	},

	shouldComponentUpdate: function(nextProps, nextState) {
		// Rerender whenever a "dirtyProp" changes
		var props = this.props;
		return some(this.dirtyProps, function(prop) {
			return !isEqual(nextProps[prop], props[prop]);
		});
	},

	/*
	If for whatever reason we are rendering again,
	we should tear down the editor and bring it up
	again.
	*/
	componentWillUpdate: function() {
		this.componentWillUnmount();
	},

	componentDidUpdate: function() {
		this.componentDidMount();
	},

	getEditorConfig: function() {
		return {
			bounds:       this.props.bounds,
			formats:      this.props.formats,
			modules:      this.props.modules,
			placeholder:  this.props.placeholder,
			readOnly:     this.props.readOnly,
			theme:        this.props.theme,
		};
	},

	getEditor: function() {
		return this.editor;
	},

	getEditorElement: function () {
		// TODO: replace string ref with callback ref
		return ReactDOM.findDOMNode(this.refs.editor);
	},

	getEditorContents: function() {
		return this.state.value;
	},

	getEditorSelection: function() {
		return this.state.selection;
	},

	/*
	Renders an editor element, unless it has been provided one to clone.
	*/
	renderContents: function() {
		var contents = [];
		var children = React.Children.map(
			this.props.children,
			function(c) { return React.cloneElement(c, {ref: c.ref}); }
		);

		var editor = find(children, function(child) {
			return child.ref === 'editor';
		});
		contents.push(editor || React.DOM.div({
			key: 'editor-' + Math.random(),
			ref: 'editor',
			className: 'quill-contents',
			dangerouslySetInnerHTML: { __html:this.getEditorContents() }
		}));

		return contents;
	},

	render: function() {
		return React.DOM.div({
			id: this.props.id,
			style: this.props.style,
			className: ['quill'].concat(this.props.className).join(' '),
			onKeyPress: this.props.onKeyPress,
			onKeyDown: this.props.onKeyDown,
			onKeyUp: this.props.onKeyUp },
			this.renderContents()
		);
	},

	onEditorChangeText: function(value, delta, source, editor) {
		if (value !== this.getEditorContents()) {
			this.setState({ value: value });
			if (this.props.onChange) {
				this.props.onChange(value, delta, source, editor);
			}
		}
	},

	onEditorChangeSelection: function(range, source, editor) {
		var s = this.getEditorSelection() || {};
		var r = range || {};
		if (r.length !== s.length || r.index !== s.index) {
			this.setState({ selection: range });
			if (this.props.onChangeSelection) {
				this.props.onChangeSelection(range, source, editor);
			}
		}
	},

	focus: function() {
		this.editor.focus();
	},

	blur: function() {
		this.setEditorSelection(this.editor, null);
	}

});

module.exports = QuillComponent;
<commit_msg>Stricter custom editing area capabilities<commit_after>'use strict';

var React = require('react');
var ReactDOM = require('react-dom');
var QuillMixin = require('./mixin');
var find = require('lodash/find');
var some = require('lodash/some');
var isEqual = require('lodash/isEqual');
var T = React.PropTypes;

var QuillComponent = React.createClass({

	displayName: 'Quill',

	mixins: [ QuillMixin ],

	propTypes: {
		id: T.string,
		className: T.string,
		theme: T.string,
		style: T.object,
		readOnly: T.bool,
		value: T.string,
		defaultValue: T.string,
		placeholder: T.string,
		bounds: T.oneOfType([T.string, T.element]),
		onKeyPress: T.func,
		onKeyDown: T.func,
		onKeyUp: T.func,
		onChange: T.func,
		onChangeSelection: T.func,

		modules: function(props) {
			var isNotObject = T.object.apply(this, arguments);
			if (isNotObject) return isNotObject;

			if (
				props.modules &&
				props.modules.toolbar &&
				props.modules.toolbar[0] &&
				props.modules.toolbar[0].type
			) return new Error(
				'Since v1.0.0, React Quill will not create a custom toolbar for you ' +
				'anymore. Create a toolbar explictly, or let Quill create one. ' +
				'See: https://github.com/zenoamaro/react-quill#upgrading-to-react-quill-v1-0-0'
			);
		},

		toolbar: function(props) {
			if ('toolbar' in props) return new Error(
				'The `toolbar` prop has been deprecated. Use `modules.toolbar` instead. ' +
				'See: https://github.com/zenoamaro/react-quill#upgrading-to-react-quill-v1-0-0'
			);
		},

		formats: function(props) {
			var isNotArrayOfString = T.arrayOf(T.string).apply(this, arguments);

			if (isNotArrayOfString) return new Error(
				'You cannot specify custom `formats` anymore. Use Parchment instead.  ' +
				'See: https://github.com/zenoamaro/react-quill#upgrading-to-react-quill-v1-0-0'
			);
		},

		styles: function(props) {
			if ('styles' in props) return new Error(
				'The `styles` prop has been deprecated. Use custom stylesheets instead. ' +
				'See: https://github.com/zenoamaro/react-quill#upgrading-to-react-quill-v1-0-0'
			);
		},

		pollInterval: function(props) {
			if ('pollInterval' in props) return new Error(
				'The `pollInterval` property does not have any effect anymore. ' +
				'You can safely remove it from your props.' +
				'See: https://github.com/zenoamaro/react-quill#upgrading-to-react-quill-v1-0-0'
			);
		}
	},

	/*
	Changing one of these props should cause a re-render.
	*/
	dirtyProps: [
		'id',
		'className',
		'style',
		'modules',
		'formats',
		'bounds',
		'theme',
	],

	getDefaultProps: function() {
		return {
			className: '',
			theme: 'snow',
			modules: {},
		};
	},

	/*
	We consider the component to be controlled if
	whenever `value` is being sent in props.
	*/
	isControlled: function() {
		return 'value' in this.props;
	},

	getInitialState: function() {
		return {
			value: this.isControlled()
				? this.props.value
				: this.props.defaultValue
		};
	},

	componentWillReceiveProps: function(nextProps) {
		var editor = this.editor;
		// If the component is unmounted and mounted too quickly
		// an error is thrown in setEditorContents since editor is
		// still undefined. Must check if editor is undefined
		// before performing this call.
		if (editor) {
			// Update only if we've been passed a new `value`.
			// This leaves components using `defaultValue` alone.
			if ('value' in nextProps) {
				// NOTE: Seeing that Quill is missing a way to prevent
				//       edits, we have to settle for a hybrid between
				//       controlled and uncontrolled mode. We can't prevent
				//       the change, but we'll still override content
				//       whenever `value` differs from current state.
				if (nextProps.value !== this.getEditorContents()) {
					this.setEditorContents(editor, nextProps.value);
				}
			}
			// We can update readOnly state in-place.
			if ('readOnly' in nextProps) {
				if (nextProps.readOnly !== this.props.readOnly) {
					this.setEditorReadOnly(editor, nextProps.readOnly);
				}
			}
		}
	},

	componentDidMount: function() {
		var editor = this.createEditor(
			this.getEditorElement(),
			this.getEditorConfig()
		);
		this.editor = editor;
	},

	componentWillUnmount: function() {
		// NOTE: Don't set the state to null here
		//       as it would generate a loop.
		var e = this.getEditor();
		if (e) this.unhookEditor(e);
	},

	shouldComponentUpdate: function(nextProps, nextState) {
		// Rerender whenever a "dirtyProp" changes
		var props = this.props;
		return some(this.dirtyProps, function(prop) {
			return !isEqual(nextProps[prop], props[prop]);
		});
	},

	/*
	If for whatever reason we are rendering again,
	we should tear down the editor and bring it up
	again.
	*/
	componentWillUpdate: function() {
		this.componentWillUnmount();
	},

	componentDidUpdate: function() {
		this.componentDidMount();
	},

	getEditorConfig: function() {
		return {
			bounds:       this.props.bounds,
			formats:      this.props.formats,
			modules:      this.props.modules,
			placeholder:  this.props.placeholder,
			readOnly:     this.props.readOnly,
			theme:        this.props.theme,
		};
	},

	getEditor: function() {
		return this.editor;
	},

	getEditorElement: function () {
		// TODO: replace string ref with callback ref
		return ReactDOM.findDOMNode(this.refs.editor);
	},

	getEditorContents: function() {
		return this.state.value;
	},

	getEditorSelection: function() {
		return this.state.selection;
	},

	/*
	Renders an editor area, unless it has been provided one to clone.
	*/
	renderEditingArea: function() {
		var children = this.props.children;

		var properties = {
			ref: 'editor',
			dangerouslySetInnerHTML: { __html:this.getEditorContents() }
		};

		if (React.Children.count(children) === 0) {
			return React.DOM.div(properties);
		}

		var editor = React.Children.only(children);
		return React.cloneElement(editor, properties);
	},

	render: function() {
		return React.DOM.div({
			id: this.props.id,
			style: this.props.style,
			className: ['quill'].concat(this.props.className).join(' '),
			onKeyPress: this.props.onKeyPress,
			onKeyDown: this.props.onKeyDown,
			onKeyUp: this.props.onKeyUp },
			this.renderEditingArea()
		);
	},

	onEditorChangeText: function(value, delta, source, editor) {
		if (value !== this.getEditorContents()) {
			this.setState({ value: value });
			if (this.props.onChange) {
				this.props.onChange(value, delta, source, editor);
			}
		}
	},

	onEditorChangeSelection: function(range, source, editor) {
		var s = this.getEditorSelection() || {};
		var r = range || {};
		if (r.length !== s.length || r.index !== s.index) {
			this.setState({ selection: range });
			if (this.props.onChangeSelection) {
				this.props.onChangeSelection(range, source, editor);
			}
		}
	},

	focus: function() {
		this.editor.focus();
	},

	blur: function() {
		this.setEditorSelection(this.editor, null);
	}

});

module.exports = QuillComponent;
<|endoftext|>


<commit_before>/**
 * @classdesc
 * Renderer class based on HTML5 Canvas2d for maps.
 * @class
 * @protected
 * @memberOf maptalks.renderer.map
 * @name Canvas
 * @extends {Z.renderer.map.Renderer}
 * @param {maptalks.Map} map - map for the renderer
 */
Z.renderer.map.Canvas = Z.renderer.map.Renderer.extend(/** @lends Z.renderer.map.Canvas.prototype */{
    initialize:function (map) {
        this.map = map;
        //container is a <canvas> element
        this._isCanvasContainer = !!map._containerDOM.getContext;
        this._registerEvents();
    },

    isCanvasRender:function () {
        return true;
    },

    /**
     * 基于Canvas的渲染方法, layers总定义了要渲染的图层
     */
    render:function () {
        this.map._fireEvent('renderstart', {'context' : this._context});
        if (!this._canvas) {
            this._createCanvas();
        }
        var zoom = this.map.getZoom();
        var layers = this._getAllLayerToTransform();

        if (!this._updateCanvasSize()) {
            this._clearCanvas();
        }

        var mwidth = this._canvas.width,
            mheight = this._canvas.height;
        this._drawBackground();

        for (var i = 0, len = layers.length; i < len; i++) {
            if (!layers[i].isVisible() || !layers[i].isCanvasRender()) {
                continue;
            }
            var renderer = layers[i]._getRenderer();
            if (renderer && renderer.getRenderZoom() === zoom) {
                var layerImage = this._getLayerImage(layers[i]);
                if (layerImage && layerImage['image']) {
                    this._drawLayerCanvasImage(layers[i], layerImage, mwidth, mheight);
                }
            }
        }

        this.map._fireEvent('renderend', {'context' : this._context});
    },

    onZoomStart:function (options, fn) {
        if (Z.Browser.ielt9) {
            fn.call(this);
            return;
        }
        var map = this.map;
        this._clearCanvas();
        if (!map.options['zoomAnimation']) {
            fn.call(this);
            return;
        }
        var baseLayer = map.getBaseLayer(),
            baseLayerImage = this._getLayerImage(baseLayer);
        if (baseLayerImage) {
            this._storeBackground(baseLayerImage);
        }
        var layersToTransform = map.options['layerZoomAnimation'] ? null : [baseLayer],
            matrix;
        if (options.startScale === 1) {
            this._beforeTransform();
        }

        if (this._context) { this._context.save(); }
        var player = Z.Animation.animate(
            {
                'scale'  : [options.startScale, options.endScale]
            },
            {
                'easing' : 'out',
                'speed'  : options.duration
            },
            Z.Util.bind(function (frame) {
                matrix = this.getZoomMatrix(frame.styles['scale'], options.origin, Z.Browser.retina);
                if (player.playState === 'finished') {
                    this._afterTransform(matrix);
                    if (this._context) { this._context.restore(); }
                    fn.call(this);
                } else if (player.playState === 'running') {
                    this.transform(matrix, layersToTransform);
                    map._fireEvent('zooming', {'matrix' : matrix});
                }
            }, this)
        ).play();
    },

    /**
     * 对图层进行仿射变换
     * @param  {Matrix} matrix 变换矩阵
     * @param  {maptalks.Layer[]} layersToTransform 参与变换和绘制的图层
     */
    transform:function (matrix, layersToTransform) {
        var mwidth = this._canvas.width,
            mheight = this._canvas.height;
        var layers = layersToTransform || this._getAllLayerToTransform();
        this._clearCanvas();
        //automatically disable updatePointsWhileTransforming with mobile browsers.
        var transformLayers = !Z.Browser.mobile && this.map.options['layerTransforming'];
        if (!transformLayers) {
            this._context.save();
            this._applyTransform(matrix);
        }

        for (var i = 0, len = layers.length; i < len; i++) {
            if (!layers[i].isVisible()) {
                continue;
            }
            var renderer = layers[i]._getRenderer();
            if (renderer) {
                var transformed = false;
                if (renderer.transform) {
                    transformed = renderer.transform(matrix);
                }
                if (transformLayers && !transformed) {
                    this._context.save();
                    this._applyTransform(matrix);
                }

                var layerImage = this._getLayerImage(layers[i]);
                if (layerImage && layerImage['image']) {
                    this._drawLayerCanvasImage(layers[i], layerImage, mwidth, mheight);
                }
                if (transformLayers && !transformed) {
                    this._context.restore();
                }
            }
        }
        if (!transformLayers) {
            this._context.restore();
        }
    },

    updateMapSize:function (mSize) {
        if (!mSize || this._isCanvasContainer) { return; }
        var width = mSize['width'],
            height = mSize['height'];
        var panels = this.map._panels;
        panels.mapWrapper.style.width = width + 'px';
        panels.mapWrapper.style.height = height + 'px';
        panels.mapPlatform.style.width = width + 'px';
        panels.mapPlatform.style.height = height + 'px';
        panels.canvasContainer.style.width = width + 'px';
        panels.canvasContainer.style.height = height + 'px';
        panels.control.style.width = width + 'px';
        panels.control.style.height = height + 'px';
        this._updateCanvasSize();
    },

    getPanel: function () {
        if (this._isCanvasContainer) {
            return this.map._containerDOM;
        }
        return this.map._panels.mapWrapper;
    },

    toDataURL:function (mimeType) {
        return this._canvas.toDataURL(mimeType);
    },

    _getLayerImage: function (layer) {
        if (layer && layer._getRenderer() && layer._getRenderer().getCanvasImage) {
            return layer._getRenderer().getCanvasImage();
        }
        return null;
    },

    _beforeTransform: function () {
        var map = this.map;
        // redraw the map to prepare for zoom transforming.
        // if startScale is not 1 (usually by touchZoom on mobiles), it means map is already transformed and doesn't need redraw
        if (!map.options['layerZoomAnimation']) {
            var baseLayer = map.getBaseLayer(),
                baseLayerImage = this._getLayerImage(baseLayer);
            //zoom animation with better performance, only animate baseLayer, ignore other layers.
            if (baseLayerImage) {
                var width = this._canvas.width, height = this._canvas.height;
                this._drawLayerCanvasImage(baseLayer, baseLayerImage, width, height);
            }
        } else {
            //default zoom animation, animate all the layers.
            this.render();
        }
    },

    _afterTransform: function (matrix) {
        delete this._transMatrix;
        this._clearCanvas();
        this._applyTransform(matrix);
        this._drawBackground();
    },

    _applyTransform : function (matrix) {
        matrix = Z.Browser.retina ? matrix['retina'] : matrix['container'];
        matrix.applyToContext(this._context);
    },

    _getCountOfGeosToDraw: function () {
        var layers = this._getAllLayerToTransform(),
            geos,
            total = 0;
        for (var i = layers.length - 1; i >= 0; i--) {
            if ((layers[i] instanceof Z.VectorLayer) &&
                layers[i].isVisible() &&
                !layers[i].isEmpty()) {
                geos = layers[i]._getRenderer()._geosToDraw;
                if (geos) {
                    total += layers[i]._getRenderer()._geosToDraw.length;
                }
            }
        }
        return total;
    },

    /**
     * initialize container DOM of panels
     */
    initContainer:function () {
        var panels = this.map._panels;
        function createContainer(name, className, cssText, enableSelect) {
            var c = Z.DomUtil.createEl('div', className);
            if (cssText) {
                c.style.cssText = cssText;
            }
            panels[name] = c;
            if (!enableSelect) {
                Z.DomUtil.preventSelection(c);
            }
            return c;
        }
        var containerDOM = this.map._containerDOM;

        if (this._isCanvasContainer) {
            //container is a <canvas> element.
            return;
        }

        containerDOM.innerHTML = '';

        var control = createContainer('control', 'maptalks-control', null, true);
        var mapWrapper = createContainer('mapWrapper', 'maptalks-wrapper', 'position:absolute;overflow:hidden;', true);
        var mapPlatform = createContainer('mapPlatform', 'maptalks-platform', 'position:absolute;top:0px;left:0px;', true);
        var ui = createContainer('ui', 'maptalks-ui', 'position:absolute;top:0px;left:0px;border:none;', true);
        var layer = createContainer('layer', 'maptalks-layer', 'position:absolute;left:0px;top:0px;');
        var canvasContainer = createContainer('canvasContainer', 'maptalks-layer-canvas', 'position:absolute;top:0px;left:0px;border:none;');

        mapPlatform.style.zIndex = 300;
        canvasContainer.style.zIndex = 100;
        ui.style.zIndex = 300;
        control.style.zIndex = 400;

        containerDOM.appendChild(mapWrapper);

        mapPlatform.appendChild(ui);
        mapPlatform.appendChild(canvasContainer);
        mapPlatform.appendChild(layer);
        mapWrapper.appendChild(mapPlatform);
        mapWrapper.appendChild(control);

        this.resetContainer();
        var mapSize = this.map._getContainerDomSize();
        this.updateMapSize(mapSize);
    },

    _drawLayerCanvasImage:function (layer, layerImage, mwidth, mheight) {
        if (!layer || !layerImage || mwidth === 0 || mheight === 0) {
            return;
        }
        var point = layerImage['point'].multi(Z.Browser.retina ? 2 : 1);
        var canvasImage = layerImage['image'];
        if (point.x + canvasImage.width <= 0 || point.y + canvasImage.height <= 0) {
            return;
        }
        var containerPos = this._getCanvasContainerPos();
        if (containerPos) {
            var offset = this.map.offsetPlatform().multi(-1)._substract(containerPos);
            if (Z.Browser.retina) { offset._multi(2); }
            point = point.add(offset);

        }
        //opacity of the layer image
        var op = layer.options['opacity'];
        if (!Z.Util.isNumber(op)) {
            op = 1;
        }
        if (op <= 0) {
            return;
        }
        var imgOp = layerImage['opacity'];
        if (!Z.Util.isNumber(imgOp)) {
            imgOp = 1;
        }
        if (imgOp <= 0) {
            return;
        }
        var alpha = this._context.globalAlpha;

        if (op < 1) {
            this._context.globalAlpha *= op;
        }
        if (imgOp < 1) {
            this._context.globalAlpha *= imgOp;
        }

        if (Z.node) {
            var context = canvasImage.getContext('2d');
            if (context.getSvg) {
                 //canvas2svg
                canvasImage = context;
            }
        }
        // point._round();
        this._context.drawImage(canvasImage, Z.Util.round(point.x), Z.Util.round(point.y));
        this._context.globalAlpha = alpha;
    },

    _storeBackground: function (baseLayerImage) {
        if (baseLayerImage) {
            var map = this.map;
            this._canvasBg = Z.DomUtil.copyCanvas(baseLayerImage['image']);
            this._canvasBgRes = map._getResolution();
            this._canvasBgCoord = map.containerPointToCoordinate(baseLayerImage['point']);
        }
    },

    _drawBackground:function () {
        var map = this.map;
        if (this._canvasBg) {
            var scale = this._canvasBgRes / map._getResolution();
            var p = map.coordinateToContainerPoint(this._canvasBgCoord)._multi(Z.Browser.retina ? 2 : 1);
            Z.Canvas.image(this._context, this._canvasBg, p.x, p.y, this._canvasBg.width * scale, this._canvasBg.height * scale);
        }
    },

    _getAllLayerToTransform:function () {
        return this.map._getLayers();
    },

    _clearCanvas:function () {
        if (!this._canvas) {
            return;
        }
        Z.Canvas.clearRect(this._context, 0, 0, this._canvas.width, this._canvas.height);
    },

    _updateCanvasSize: function () {
        if (!this._canvas || this._isCanvasContainer) {
            return false;
        }
        var map = this.map;
        var mapSize = map.getSize();
        var canvas = this._canvas;
        var r = Z.Browser.retina ? 2 : 1;
        if (mapSize['width'] * r === canvas.width && mapSize['height'] * r === canvas.height) {
            return false;
        }
        //retina屏支持

        canvas.height = r * mapSize['height'];
        canvas.width = r * mapSize['width'];
        if (canvas.style) {
            canvas.style.width = mapSize['width'] + 'px';
            canvas.style.height = mapSize['height'] + 'px';
        }

        return true;
    },

    _createCanvas:function () {
        if (this._isCanvasContainer) {
            this._canvas = this.map._containerDOM;
        } else {
            this._canvas = Z.DomUtil.createEl('canvas');
            this._updateCanvasSize();
            this.map._panels.canvasContainer.appendChild(this._canvas);
        }
        this._context = this._canvas.getContext('2d');
    },

    /**
     * 设置地图的watcher, 用来监视地图容器的大小变化
     * @ignore
     */
    _onResize:function () {
        delete this._canvasBg;
        this.map.checkSize();
    },

    _registerEvents:function () {
        var map = this.map;
        map.on('_baselayerchangestart', function () {
            delete this._canvasBg;
        }, this);
        map.on('_baselayerload', function () {
            var baseLayer = map.getBaseLayer();
            if (!map.options['zoomBackground'] || baseLayer.getMask()) {
                delete this._canvasBg;
            }
        }, this);

        map.on('_zoomstart', function () {
            delete this._canvasBg;
            this._clearCanvas();
        }, this);
        if (typeof window !== 'undefined') {
            Z.DomUtil.on(window, 'resize', this._onResize, this);
        }
        if (!Z.Browser.mobile && Z.Browser.canvas) {
            this._onMapMouseMove = function (param) {
                if (map._isBusy() || map._moving || !map.options['hitDetect']) {
                    return;
                }
                if (this._hitDetectTimeout) {
                    Z.Util.cancelAnimFrame(this._hitDetectTimeout);
                }
                this._hitDetectTimeout = Z.Util.requestAnimFrame(function () {
                    var vp = param['viewPoint'];
                    var layers = map._getLayers();
                    var hit = false,
                        cursor;
                    for (var i = layers.length - 1; i >= 0; i--) {
                        var layer = layers[i];
                        if (layer._getRenderer() && layer._getRenderer().hitDetect) {
                            if (layer.options['cursor'] !== 'default' && layer._getRenderer().hitDetect(vp)) {
                                cursor = layer.options['cursor'];
                                hit = true;
                                break;
                            }
                        }
                    }
                    if (hit) {
                        map._trySetCursor(cursor);
                    } else {
                        map._trySetCursor('default');
                    }
                });

            };
            map.on('_mousemove', this._onMapMouseMove, this);
        }

        if (this._isCanvasContainer) {
            map.on('_moving', function () {
                this.render();
            }, this);
        } else {
            map.on('_moveend', function () {
                this._resetCanvasContainer();
                this.render();
            }, this);
        }
    }
});

Z.Map.registerRenderer('canvas', Z.renderer.map.Canvas);
<commit_msg>fixed count of geos to draw when transforming<commit_after>/**
 * @classdesc
 * Renderer class based on HTML5 Canvas2d for maps.
 * @class
 * @protected
 * @memberOf maptalks.renderer.map
 * @name Canvas
 * @extends {Z.renderer.map.Renderer}
 * @param {maptalks.Map} map - map for the renderer
 */
Z.renderer.map.Canvas = Z.renderer.map.Renderer.extend(/** @lends Z.renderer.map.Canvas.prototype */{
    initialize:function (map) {
        this.map = map;
        //container is a <canvas> element
        this._isCanvasContainer = !!map._containerDOM.getContext;
        this._registerEvents();
    },

    isCanvasRender:function () {
        return true;
    },

    /**
     * 基于Canvas的渲染方法, layers总定义了要渲染的图层
     */
    render:function () {
        this.map._fireEvent('renderstart', {'context' : this._context});
        if (!this._canvas) {
            this._createCanvas();
        }
        var zoom = this.map.getZoom();
        var layers = this._getAllLayerToTransform();

        if (!this._updateCanvasSize()) {
            this._clearCanvas();
        }

        var mwidth = this._canvas.width,
            mheight = this._canvas.height;
        this._drawBackground();

        for (var i = 0, len = layers.length; i < len; i++) {
            if (!layers[i].isVisible() || !layers[i].isCanvasRender()) {
                continue;
            }
            var renderer = layers[i]._getRenderer();
            if (renderer && renderer.getRenderZoom() === zoom) {
                var layerImage = this._getLayerImage(layers[i]);
                if (layerImage && layerImage['image']) {
                    this._drawLayerCanvasImage(layers[i], layerImage, mwidth, mheight);
                }
            }
        }

        this.map._fireEvent('renderend', {'context' : this._context});
    },

    onZoomStart:function (options, fn) {
        if (Z.Browser.ielt9) {
            fn.call(this);
            return;
        }
        var map = this.map;
        this._clearCanvas();
        if (!map.options['zoomAnimation']) {
            fn.call(this);
            return;
        }
        var baseLayer = map.getBaseLayer(),
            baseLayerImage = this._getLayerImage(baseLayer);
        if (baseLayerImage) {
            this._storeBackground(baseLayerImage);
        }
        var layersToTransform = map.options['layerZoomAnimation'] ? null : [baseLayer],
            matrix;
        if (options.startScale === 1) {
            this._beforeTransform();
        }

        if (this._context) { this._context.save(); }
        var player = Z.Animation.animate(
            {
                'scale'  : [options.startScale, options.endScale]
            },
            {
                'easing' : 'out',
                'speed'  : options.duration
            },
            Z.Util.bind(function (frame) {
                matrix = this.getZoomMatrix(frame.styles['scale'], options.origin, Z.Browser.retina);
                if (player.playState === 'finished') {
                    this._afterTransform(matrix);
                    if (this._context) { this._context.restore(); }
                    fn.call(this);
                } else if (player.playState === 'running') {
                    this.transform(matrix, layersToTransform);
                    map._fireEvent('zooming', {'matrix' : matrix});
                }
            }, this)
        ).play();
    },

    /**
     * 对图层进行仿射变换
     * @param  {Matrix} matrix 变换矩阵
     * @param  {maptalks.Layer[]} layersToTransform 参与变换和绘制的图层
     */
    transform:function (matrix, layersToTransform) {
        var mwidth = this._canvas.width,
            mheight = this._canvas.height;
        var layers = layersToTransform || this._getAllLayerToTransform();
        this._clearCanvas();
        //automatically disable updatePointsWhileTransforming with mobile browsers.
        var transformLayers = !Z.Browser.mobile && this.map.options['layerTransforming'];
        if (!transformLayers) {
            this._context.save();
            this._applyTransform(matrix);
        }

        for (var i = 0, len = layers.length; i < len; i++) {
            if (!layers[i].isVisible()) {
                continue;
            }
            var renderer = layers[i]._getRenderer();
            if (renderer) {
                var transformed = false;
                if (renderer.transform) {
                    transformed = renderer.transform(matrix);
                }
                if (transformLayers && !transformed) {
                    this._context.save();
                    this._applyTransform(matrix);
                }

                var layerImage = this._getLayerImage(layers[i]);
                if (layerImage && layerImage['image']) {
                    this._drawLayerCanvasImage(layers[i], layerImage, mwidth, mheight);
                }
                if (transformLayers && !transformed) {
                    this._context.restore();
                }
            }
        }
        if (!transformLayers) {
            this._context.restore();
        }
    },

    updateMapSize:function (mSize) {
        if (!mSize || this._isCanvasContainer) { return; }
        var width = mSize['width'],
            height = mSize['height'];
        var panels = this.map._panels;
        panels.mapWrapper.style.width = width + 'px';
        panels.mapWrapper.style.height = height + 'px';
        panels.mapPlatform.style.width = width + 'px';
        panels.mapPlatform.style.height = height + 'px';
        panels.canvasContainer.style.width = width + 'px';
        panels.canvasContainer.style.height = height + 'px';
        panels.control.style.width = width + 'px';
        panels.control.style.height = height + 'px';
        this._updateCanvasSize();
    },

    getPanel: function () {
        if (this._isCanvasContainer) {
            return this.map._containerDOM;
        }
        return this.map._panels.mapWrapper;
    },

    toDataURL:function (mimeType) {
        return this._canvas.toDataURL(mimeType);
    },

    _getLayerImage: function (layer) {
        if (layer && layer._getRenderer() && layer._getRenderer().getCanvasImage) {
            return layer._getRenderer().getCanvasImage();
        }
        return null;
    },

    _beforeTransform: function () {
        var map = this.map;
        // redraw the map to prepare for zoom transforming.
        // if startScale is not 1 (usually by touchZoom on mobiles), it means map is already transformed and doesn't need redraw
        if (!map.options['layerZoomAnimation']) {
            var baseLayer = map.getBaseLayer(),
                baseLayerImage = this._getLayerImage(baseLayer);
            //zoom animation with better performance, only animate baseLayer, ignore other layers.
            if (baseLayerImage) {
                var width = this._canvas.width, height = this._canvas.height;
                this._drawLayerCanvasImage(baseLayer, baseLayerImage, width, height);
            }
        } else {
            //default zoom animation, animate all the layers.
            this.render();
        }
    },

    _afterTransform: function (matrix) {
        delete this._transMatrix;
        this._clearCanvas();
        this._applyTransform(matrix);
        this._drawBackground();
    },

    _applyTransform : function (matrix) {
        matrix = Z.Browser.retina ? matrix['retina'] : matrix['container'];
        matrix.applyToContext(this._context);
    },

    _getCountOfGeosToDraw: function () {
        var layers = this._getAllLayerToTransform(),
            geos, renderer,
            total = 0;
        for (var i = layers.length - 1; i >= 0; i--) {
            renderer = layers[i]._getRenderer();
            if ((layers[i] instanceof Z.VectorLayer) &&
                layers[i].isVisible() && !layers[i].isEmpty() && renderer._hasPointSymbolizer) {
                geos = renderer._geosToDraw;
                if (geos) {
                    total += renderer._geosToDraw.length;
                }
            }
        }
        return total;
    },

    /**
     * initialize container DOM of panels
     */
    initContainer:function () {
        var panels = this.map._panels;
        function createContainer(name, className, cssText, enableSelect) {
            var c = Z.DomUtil.createEl('div', className);
            if (cssText) {
                c.style.cssText = cssText;
            }
            panels[name] = c;
            if (!enableSelect) {
                Z.DomUtil.preventSelection(c);
            }
            return c;
        }
        var containerDOM = this.map._containerDOM;

        if (this._isCanvasContainer) {
            //container is a <canvas> element.
            return;
        }

        containerDOM.innerHTML = '';

        var control = createContainer('control', 'maptalks-control', null, true);
        var mapWrapper = createContainer('mapWrapper', 'maptalks-wrapper', 'position:absolute;overflow:hidden;', true);
        var mapPlatform = createContainer('mapPlatform', 'maptalks-platform', 'position:absolute;top:0px;left:0px;', true);
        var ui = createContainer('ui', 'maptalks-ui', 'position:absolute;top:0px;left:0px;border:none;', true);
        var layer = createContainer('layer', 'maptalks-layer', 'position:absolute;left:0px;top:0px;');
        var canvasContainer = createContainer('canvasContainer', 'maptalks-layer-canvas', 'position:absolute;top:0px;left:0px;border:none;');

        mapPlatform.style.zIndex = 300;
        canvasContainer.style.zIndex = 100;
        ui.style.zIndex = 300;
        control.style.zIndex = 400;

        containerDOM.appendChild(mapWrapper);

        mapPlatform.appendChild(ui);
        mapPlatform.appendChild(canvasContainer);
        mapPlatform.appendChild(layer);
        mapWrapper.appendChild(mapPlatform);
        mapWrapper.appendChild(control);

        this.resetContainer();
        var mapSize = this.map._getContainerDomSize();
        this.updateMapSize(mapSize);
    },

    _drawLayerCanvasImage:function (layer, layerImage, mwidth, mheight) {
        if (!layer || !layerImage || mwidth === 0 || mheight === 0) {
            return;
        }
        var point = layerImage['point'].multi(Z.Browser.retina ? 2 : 1);
        var canvasImage = layerImage['image'];
        if (point.x + canvasImage.width <= 0 || point.y + canvasImage.height <= 0) {
            return;
        }
        var containerPos = this._getCanvasContainerPos();
        if (containerPos) {
            var offset = this.map.offsetPlatform().multi(-1)._substract(containerPos);
            if (Z.Browser.retina) { offset._multi(2); }
            point = point.add(offset);

        }
        //opacity of the layer image
        var op = layer.options['opacity'];
        if (!Z.Util.isNumber(op)) {
            op = 1;
        }
        if (op <= 0) {
            return;
        }
        var imgOp = layerImage['opacity'];
        if (!Z.Util.isNumber(imgOp)) {
            imgOp = 1;
        }
        if (imgOp <= 0) {
            return;
        }
        var alpha = this._context.globalAlpha;

        if (op < 1) {
            this._context.globalAlpha *= op;
        }
        if (imgOp < 1) {
            this._context.globalAlpha *= imgOp;
        }

        if (Z.node) {
            var context = canvasImage.getContext('2d');
            if (context.getSvg) {
                 //canvas2svg
                canvasImage = context;
            }
        }
        // point._round();
        this._context.drawImage(canvasImage, Z.Util.round(point.x), Z.Util.round(point.y));
        this._context.globalAlpha = alpha;
    },

    _storeBackground: function (baseLayerImage) {
        if (baseLayerImage) {
            var map = this.map;
            this._canvasBg = Z.DomUtil.copyCanvas(baseLayerImage['image']);
            this._canvasBgRes = map._getResolution();
            this._canvasBgCoord = map.containerPointToCoordinate(baseLayerImage['point']);
        }
    },

    _drawBackground:function () {
        var map = this.map;
        if (this._canvasBg) {
            var scale = this._canvasBgRes / map._getResolution();
            var p = map.coordinateToContainerPoint(this._canvasBgCoord)._multi(Z.Browser.retina ? 2 : 1);
            Z.Canvas.image(this._context, this._canvasBg, p.x, p.y, this._canvasBg.width * scale, this._canvasBg.height * scale);
        }
    },

    _getAllLayerToTransform:function () {
        return this.map._getLayers();
    },

    _clearCanvas:function () {
        if (!this._canvas) {
            return;
        }
        Z.Canvas.clearRect(this._context, 0, 0, this._canvas.width, this._canvas.height);
    },

    _updateCanvasSize: function () {
        if (!this._canvas || this._isCanvasContainer) {
            return false;
        }
        var map = this.map;
        var mapSize = map.getSize();
        var canvas = this._canvas;
        var r = Z.Browser.retina ? 2 : 1;
        if (mapSize['width'] * r === canvas.width && mapSize['height'] * r === canvas.height) {
            return false;
        }
        //retina屏支持

        canvas.height = r * mapSize['height'];
        canvas.width = r * mapSize['width'];
        if (canvas.style) {
            canvas.style.width = mapSize['width'] + 'px';
            canvas.style.height = mapSize['height'] + 'px';
        }

        return true;
    },

    _createCanvas:function () {
        if (this._isCanvasContainer) {
            this._canvas = this.map._containerDOM;
        } else {
            this._canvas = Z.DomUtil.createEl('canvas');
            this._updateCanvasSize();
            this.map._panels.canvasContainer.appendChild(this._canvas);
        }
        this._context = this._canvas.getContext('2d');
    },

    /**
     * 设置地图的watcher, 用来监视地图容器的大小变化
     * @ignore
     */
    _onResize:function () {
        delete this._canvasBg;
        this.map.checkSize();
    },

    _registerEvents:function () {
        var map = this.map;
        map.on('_baselayerchangestart', function () {
            delete this._canvasBg;
        }, this);
        map.on('_baselayerload', function () {
            var baseLayer = map.getBaseLayer();
            if (!map.options['zoomBackground'] || baseLayer.getMask()) {
                delete this._canvasBg;
            }
        }, this);

        map.on('_zoomstart', function () {
            delete this._canvasBg;
            this._clearCanvas();
        }, this);
        if (typeof window !== 'undefined') {
            Z.DomUtil.on(window, 'resize', this._onResize, this);
        }
        if (!Z.Browser.mobile && Z.Browser.canvas) {
            this._onMapMouseMove = function (param) {
                if (map._isBusy() || map._moving || !map.options['hitDetect']) {
                    return;
                }
                if (this._hitDetectTimeout) {
                    Z.Util.cancelAnimFrame(this._hitDetectTimeout);
                }
                this._hitDetectTimeout = Z.Util.requestAnimFrame(function () {
                    var vp = param['viewPoint'];
                    var layers = map._getLayers();
                    var hit = false,
                        cursor;
                    for (var i = layers.length - 1; i >= 0; i--) {
                        var layer = layers[i];
                        if (layer._getRenderer() && layer._getRenderer().hitDetect) {
                            if (layer.options['cursor'] !== 'default' && layer._getRenderer().hitDetect(vp)) {
                                cursor = layer.options['cursor'];
                                hit = true;
                                break;
                            }
                        }
                    }
                    if (hit) {
                        map._trySetCursor(cursor);
                    } else {
                        map._trySetCursor('default');
                    }
                });

            };
            map.on('_mousemove', this._onMapMouseMove, this);
        }

        if (this._isCanvasContainer) {
            map.on('_moving', function () {
                this.render();
            }, this);
        } else {
            map.on('_moveend', function () {
                this._resetCanvasContainer();
                this.render();
            }, this);
        }
    }
});

Z.Map.registerRenderer('canvas', Z.renderer.map.Canvas);
<|endoftext|>


<commit_before>import marked from "marked";

export function i18nMarked(key, ...args) {
  return marked(i18n(key, ...args));
}<commit_msg>Removed unused file<commit_after><|endoftext|>


<commit_before>//a set of helper functions, some that we've written, others that we've borrowed

MG.convert = {};

MG.convert.date = function(data, accessor, time_format) {
    time_format = (typeof time_format === "undefined") ? '%Y-%m-%d' : time_format;
    data = data.map(function(d) {
        var fff = d3.time.format(time_format);
        d[accessor] = fff.parse(d[accessor]);
        return d;
    });

    return data;
};

MG.convert.number = function(data, accessor) {
    data = data.map(function(d) {
        d[accessor] = Number(d[accessor]);
        return d;
    });

    return data;
};

function mg_get_svg_child_of(selector_or_node) {
    return d3.select(selector_or_node).select('svg');
}

function mg_strip_punctuation(s) {
    var processed_s;
    if (typeof(s) == 'string'){
        processed_s = s;
    } else {
        // args.target is
        if (s.id != ''){
            processed_s = s.id;
        } else if (args.target.className != ''){
            processed_s = s.className;
        } else if (args.target.nodeName !=''){
            processed_s = s.nodeName;
        } else {
            console.warn('The specified target element ' + s + ' has no unique attributes.');
        }
    }
    var punctuationless = processed_s.replace(/[^a-zA-Z0-9 _]+/g, '');
    var finalString = punctuationless.replace(/ +?/g, "");
    return finalString;
}

function get_pixel_dimension(target, dimension) {
    return Number(d3.select(target).style(dimension).replace(/px/g, ''));
}

function get_width(target) {
    return get_pixel_dimension(target, 'width');
}

function get_height(target) {
    return get_pixel_dimension(target, 'height');
}

function isNumeric(n) {
    return !isNaN(parseFloat(n)) && isFinite(n);
}

var each = function(obj, iterator, context) {
    // yanked out of underscore
    if (obj === null) return obj;
    if (Array.prototype.forEach && obj.forEach === Array.prototype.forEach) {
      obj.forEach(iterator, context);
    } else if (obj.length === +obj.length) {
      for (var i = 0, length = obj.length; i < length; i++) {
        if (iterator.call(context, obj[i], i, obj) === breaker) return;
      }
    } else {
      for (var k in obj) {
        if (iterator.call(context, obj[k], k, obj) === breaker) return;
      }
    }

    return obj;
};

function merge_with_defaults(obj) {
    // taken from underscore
    each(Array.prototype.slice.call(arguments, 1), function(source) {
      if (source) {
        for (var prop in source) {
          if (obj[prop] === void 0) obj[prop] = source[prop];
        }
      }
    });

    return obj;
}

function number_of_values(data, accessor, value) {
    var values = data.filter(function(d) {
        return d[accessor] === value;
    });

    return values.length;
}

function has_values_below(data, accessor, value) {
    var values = data.filter(function(d) {
        return d[accessor] <= value;
    });

    return values.length > 0;
}

function has_too_many_zeros(data, accessor, zero_count) {
    return number_of_values(data, accessor, 0) >= zero_count;
}

//deep copy
//http://stackoverflow.com/questions/728360/most-elegant-way-to-clone-a-javascript-object
MG.clone = function(obj) {
    var copy;

    // Handle the 3 simple types, and null or undefined
    if (null === obj || "object" !== typeof obj) return obj;

    // Handle Date
    if (obj instanceof Date) {
        copy = new Date();
        copy.setTime(obj.getTime());
        return copy;
    }

    // Handle Array
    if (obj instanceof Array) {
        copy = [];
        for (var i = 0, len = obj.length; i < len; i++) {
            copy[i] = MG.clone(obj[i]);
        }
        return copy;
    }

    // Handle Object
    if (obj instanceof Object) {
        copy = {};
        for (var attr in obj) {
            if (obj.hasOwnProperty(attr)) copy[attr] = MG.clone(obj[attr]);
        }
        return copy;
    }

    throw new Error("Unable to copy obj! Its type isn't supported.");
};

//give us the difference of two int arrays
//http://radu.cotescu.com/javascript-diff-function/
function arrDiff(a,b) {
    var seen = [],
        diff = [],
        i;
    for (i = 0; i < b.length; i++)
        seen[b[i]] = true;
    for (i = 0; i < a.length; i++)
        if (!seen[a[i]])
            diff.push(a[i]);
    return diff;
}

/**
    Print warning message to the console when a feature has been scheduled for removal

    @author Dan de Havilland (github.com/dandehavilland)
    @date 2014-12
*/
function warnDeprecation(message, untilVersion) {
  console.warn('Deprecation: ' + message + (untilVersion ? '. This feature will be removed in ' + untilVersion + '.' : ' the near future.'));
  console.trace();
}

/**
    Truncate a string to fit within an SVG text node
    CSS text-overlow doesn't apply to SVG <= 1.2

    @author Dan de Havilland (github.com/dandehavilland)
    @date 2014-12-02
*/
function truncate_text(textObj, textString, width) {
  var bbox,
    position = 0;

  textObj.textContent = textString;
  bbox = textObj.getBBox();

  while (bbox.width > width) {
    textObj.textContent = textString.slice(0, --position) + '...';
    bbox = textObj.getBBox();

    if (textObj.textContent === '...') {
      break;
    }
  }
}

/**
  Wrap the contents of a text node to a specific width

  Adapted from bl.ocks.org/mbostock/7555321

  @author Mike Bostock
  @author Dan de Havilland
  @date 2015-01-14
*/
function wrapText(text, width, token, tspanAttrs) {
  text.each(function() {
    var text = d3.select(this),
        words = text.text().split(token || /\s+/).reverse(),
        word,
        line = [],
        lineNumber = 0,
        lineHeight = 1.1, // ems
        y = text.attr("y"),
        dy = 0,
        tspan = text.text(null)
          .append("tspan")
          .attr("x", 0)
          .attr("y", dy + "em")
          .attr(tspanAttrs || {});

    while (!!(word = words.pop())) {
      line.push(word);
      tspan.text(line.join(" "));
      if (width === null || tspan.node().getComputedTextLength() > width) {
        line.pop();
        tspan.text(line.join(" "));
        line = [word];
        tspan = text
            .append("tspan")
            .attr("x", 0)
            .attr("y", ++lineNumber * lineHeight + dy + "em")
            .attr(tspanAttrs || {})
            .text(word);
      }
    }
  });
}
<commit_msg>Match existing style<commit_after>//a set of helper functions, some that we've written, others that we've borrowed

MG.convert = {};

MG.convert.date = function(data, accessor, time_format) {
    time_format = (typeof time_format === "undefined") ? '%Y-%m-%d' : time_format;
    data = data.map(function(d) {
        var fff = d3.time.format(time_format);
        d[accessor] = fff.parse(d[accessor]);
        return d;
    });

    return data;
};

MG.convert.number = function(data, accessor) {
    data = data.map(function(d) {
        d[accessor] = Number(d[accessor]);
        return d;
    });

    return data;
};

function mg_get_svg_child_of(selector_or_node) {
    return d3.select(selector_or_node).select('svg');
}

function mg_strip_punctuation(s) {
    var processed_s;

    if (typeof(s) == 'string') {
        processed_s = s;
    } else {
        // args.target is
        if (s.id != '') {
            processed_s = s.id;
        } else if (args.target.className != '') {
            processed_s = s.className;
        } else if (args.target.nodeName !='') {
            processed_s = s.nodeName;
        } else {
            console.warn('The specified target element ' + s + ' has no unique attributes.');
        }
    }

    var punctuationless = processed_s.replace(/[^a-zA-Z0-9 _]+/g, '');
    var finalString = punctuationless.replace(/ +?/g, "");

    return finalString;
}

function get_pixel_dimension(target, dimension) {
    return Number(d3.select(target).style(dimension).replace(/px/g, ''));
}

function get_width(target) {
    return get_pixel_dimension(target, 'width');
}

function get_height(target) {
    return get_pixel_dimension(target, 'height');
}

function isNumeric(n) {
    return !isNaN(parseFloat(n)) && isFinite(n);
}

var each = function(obj, iterator, context) {
    // yanked out of underscore
    if (obj === null) return obj;
    if (Array.prototype.forEach && obj.forEach === Array.prototype.forEach) {
      obj.forEach(iterator, context);
    } else if (obj.length === +obj.length) {
      for (var i = 0, length = obj.length; i < length; i++) {
        if (iterator.call(context, obj[i], i, obj) === breaker) return;
      }
    } else {
      for (var k in obj) {
        if (iterator.call(context, obj[k], k, obj) === breaker) return;
      }
    }

    return obj;
};

function merge_with_defaults(obj) {
    // taken from underscore
    each(Array.prototype.slice.call(arguments, 1), function(source) {
      if (source) {
        for (var prop in source) {
          if (obj[prop] === void 0) obj[prop] = source[prop];
        }
      }
    });

    return obj;
}

function number_of_values(data, accessor, value) {
    var values = data.filter(function(d) {
        return d[accessor] === value;
    });

    return values.length;
}

function has_values_below(data, accessor, value) {
    var values = data.filter(function(d) {
        return d[accessor] <= value;
    });

    return values.length > 0;
}

function has_too_many_zeros(data, accessor, zero_count) {
    return number_of_values(data, accessor, 0) >= zero_count;
}

//deep copy
//http://stackoverflow.com/questions/728360/most-elegant-way-to-clone-a-javascript-object
MG.clone = function(obj) {
    var copy;

    // Handle the 3 simple types, and null or undefined
    if (null === obj || "object" !== typeof obj) return obj;

    // Handle Date
    if (obj instanceof Date) {
        copy = new Date();
        copy.setTime(obj.getTime());
        return copy;
    }

    // Handle Array
    if (obj instanceof Array) {
        copy = [];
        for (var i = 0, len = obj.length; i < len; i++) {
            copy[i] = MG.clone(obj[i]);
        }
        return copy;
    }

    // Handle Object
    if (obj instanceof Object) {
        copy = {};
        for (var attr in obj) {
            if (obj.hasOwnProperty(attr)) copy[attr] = MG.clone(obj[attr]);
        }
        return copy;
    }

    throw new Error("Unable to copy obj! Its type isn't supported.");
};

//give us the difference of two int arrays
//http://radu.cotescu.com/javascript-diff-function/
function arrDiff(a,b) {
    var seen = [],
        diff = [],
        i;
    for (i = 0; i < b.length; i++)
        seen[b[i]] = true;
    for (i = 0; i < a.length; i++)
        if (!seen[a[i]])
            diff.push(a[i]);
    return diff;
}

/**
    Print warning message to the console when a feature has been scheduled for removal

    @author Dan de Havilland (github.com/dandehavilland)
    @date 2014-12
*/
function warnDeprecation(message, untilVersion) {
  console.warn('Deprecation: ' + message + (untilVersion ? '. This feature will be removed in ' + untilVersion + '.' : ' the near future.'));
  console.trace();
}

/**
    Truncate a string to fit within an SVG text node
    CSS text-overlow doesn't apply to SVG <= 1.2

    @author Dan de Havilland (github.com/dandehavilland)
    @date 2014-12-02
*/
function truncate_text(textObj, textString, width) {
  var bbox,
    position = 0;

  textObj.textContent = textString;
  bbox = textObj.getBBox();

  while (bbox.width > width) {
    textObj.textContent = textString.slice(0, --position) + '...';
    bbox = textObj.getBBox();

    if (textObj.textContent === '...') {
      break;
    }
  }
}

/**
  Wrap the contents of a text node to a specific width

  Adapted from bl.ocks.org/mbostock/7555321

  @author Mike Bostock
  @author Dan de Havilland
  @date 2015-01-14
*/
function wrapText(text, width, token, tspanAttrs) {
  text.each(function() {
    var text = d3.select(this),
        words = text.text().split(token || /\s+/).reverse(),
        word,
        line = [],
        lineNumber = 0,
        lineHeight = 1.1, // ems
        y = text.attr("y"),
        dy = 0,
        tspan = text.text(null)
          .append("tspan")
          .attr("x", 0)
          .attr("y", dy + "em")
          .attr(tspanAttrs || {});

    while (!!(word = words.pop())) {
      line.push(word);
      tspan.text(line.join(" "));
      if (width === null || tspan.node().getComputedTextLength() > width) {
        line.pop();
        tspan.text(line.join(" "));
        line = [word];
        tspan = text
            .append("tspan")
            .attr("x", 0)
            .attr("y", ++lineNumber * lineHeight + dy + "em")
            .attr(tspanAttrs || {})
            .text(word);
      }
    }
  });
}
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react'
import firebase from 'firebase'
import { Link } from 'react-router'

class MainView extends Component {
  constructor (props) {
    super(props)
    this.state = {
      forumSections: [],
      sectionsLoaded: false
    }
  }

  componentWillMount () {
    this.fetchItems()
    firebase.database().ref('forumSections/courseId').remove()
  }

  fetchItems () {
    this.setState({
      forumSections: [],
      sectionsLoaded: false
    })
    firebase.database().ref('forumSections/')
    .once('value')
    .then(snapshot => {
      const object = snapshot.val()
      if (object !== null) {
        const forumSections = Object.keys(object).map(id => ({ ...object[id], id }))
        this.setState({ forumSections, sectionsLoaded: true })
        this.fetchInfo()
      } else {
        this.setState({ sectionsLoaded: true })
      }
    })
  }

  fetchInfo () {
    const { forumSections } = this.state
    const promises = forumSections.map(item => {
      return firebase.database().ref('courses/' + item.id)
      .once('value')
      .then(snapshot => {
        const object = snapshot.val()
        const courseFromId = object
        if (object !== null) {
          item.discipline = courseFromId.discipline
          item.name = courseFromId.name
          console.log()
          return (item)
        } else {
          this.setState({ sectionsLoaded: true })
        }
      })
    })
    Promise.all(promises).then(result => {
      this.setState({
        forumSections: result,
        forumSectionsLoaded: true
      })
    })
  }

  renderSectionsList (filteredForumSections) {
    const { location } = this.props
    return filteredForumSections.map((item, i) =>

          <Link to={{ pathname: `${location.pathname}/section/${item.id}` }}>{item.name}</Link>

    )
  }

  renderTable (discipline) {
    const { forumSections = [] } = this.state
    forumSections.forEach(item => console.log(item) )
    const filteredForumSections = forumSections.filter((item) =>
      item.discipline === discipline)
    return

      {!!filteredForumSections.length &&

              Name

            {this.renderSectionsList(filteredForumSections)}

      }
      {/* {!filteredForumSections.length &&
 No sections
} */}

  }

  render () {
    return (

          Sections
          Dota 2
          {this.renderTable('Dota2')}
          CS:GO
          {this.renderTable('CS:GO')}
          League of Legends
          {this.renderTable('LoL')}

    )
  }
}

MainView.propTypes = {
  location: PropTypes.object
}

export default MainView
<commit_msg>fixCoursePage<commit_after>import React, { Component, PropTypes } from 'react'
import firebase from 'firebase'
import { Link } from 'react-router'

class MainView extends Component {
  constructor (props) {
    super(props)
    this.state = {
      forumSections: [],
      sectionsLoaded: false
    }
  }

  componentWillMount () {
    this.fetchItems()
  }

  fetchItems () {
    this.setState({
      forumSections: [],
      sectionsLoaded: false
    })
    firebase.database().ref('forumSections/')
    .once('value')
    .then(snapshot => {
      const object = snapshot.val()
      if (object !== null) {
        const forumSections = Object.keys(object).map(id => ({ ...object[id], id }))
        this.setState({ forumSections, sectionsLoaded: true })
        this.fetchInfo()
      } else {
        this.setState({ sectionsLoaded: true })
      }
    })
  }

  fetchInfo () {
    const { forumSections } = this.state
    const promises = forumSections.map(item => {
      return firebase.database().ref('courses/' + item.id)
      .once('value')
      .then(snapshot => {
        const object = snapshot.val()
        const courseFromId = object
        if (object !== null) {
          item.discipline = courseFromId.discipline
          item.name = courseFromId.name
          return (item)
        } else {
          this.setState({ sectionsLoaded: true })
        }
      })
    })
    Promise.all(promises).then(result => {
      this.setState({
        forumSections: result,
        forumSectionsLoaded: true
      })
    })
  }

  renderSectionsList (filteredForumSections) {
    const { location } = this.props
    return filteredForumSections.map((item, i) =>

          <Link to={{ pathname: `${location.pathname}/section/${item.id}` }}>{item.name}</Link>

    )
  }

  renderTable (discipline) {
    const { forumSections = [] } = this.state
    const filteredForumSections = forumSections.filter((item) =>
      item.discipline === discipline)
    return

      {!!filteredForumSections.length &&

              Name

            {this.renderSectionsList(filteredForumSections)}

      }
      {/* {!filteredForumSections.length &&
 No sections
} */}

  }

  render () {
    return (

          Sections
          Dota 2
          {this.renderTable('Dota2')}
          CS:GO
          {this.renderTable('CS:GO')}
          League of Legends
          {this.renderTable('LoL')}

    )
  }
}

MainView.propTypes = {
  location: PropTypes.object
}

export default MainView
<|endoftext|>