<commit_before>const gulp = require('gulp');

// Pug
const pug = require('gulp-pug');
const fs = require('fs');
const data = require('gulp-data');
const path = require('path');

// HTML
const htmlhint = require('gulp-htmlhint');
const ssi = require('browsersync-ssi');

// CSS
const sass = require('gulp-sass');
const sassGlob = require('gulp-sass-glob');
const postcss = require('gulp-postcss');
const flexBugsFixes = require('postcss-flexbugs-fixes');
const autoprefixer = require('autoprefixer');
const cleanCSS = require('gulp-clean-css');
const gulpStylelint = require('gulp-stylelint');

// JS
const uglify = require('gulp-uglify');
const browserify = require('browserify');
const babelify = require('babelify');
const source = require('vinyl-source-stream');
const buffer = require('vinyl-buffer');
const watchify = require('watchify');
const vueify = require('vueify');
const envify = require('envify/custom');

// Image
const imagemin = require('gulp-imagemin');

// SVG sprite
const svgSprite = require('gulp-svg-sprite');

// Styleguide
const aigis = require('gulp-aigis');

// Utility
const cache = require('gulp-cached');
const changed = require('gulp-changed');
const sourcemaps = require('gulp-sourcemaps');
const runSequence = require('run-sequence');
const browserSync = require('browser-sync');
const plumber = require('gulp-plumber');
const notify = require('gulp-notify');
const gulpif = require('gulp-if');
const rimraf = require('rimraf');

/**
 * 開発用ディレクトリ
 */
const src = {
  root: 'src/',
  html: ['src/**/*.pug', '!src/**/_*.pug'],
  ssi: 'public/**/*.html',
  data: 'src/_data/',
  css: 'src/**/*.scss',
  styleguideWatch: ['src/**/*.scss', 'src/**/*.md'],
  js: 'src/assets/js/site.js',
  image: 'src/assets/img/**/*.{png,jpg,gif,svg}',
  imageWatch: 'src/assets/img/**/*',
  svgSprite: 'src/assets/svg/**/*.svg',
  public: 'public/**/*',
};

/**
 * テスト用ディレクトリ
 */
const dest = {
  root: 'htdocs/',
  image: 'htdocs/assets/img/',
  js: 'htdocs/assets/js/',
  svgSprite: 'htdocs/assets/svg/',
};

/**
 * 環境変数を設定します。
 */
const env = process.env.APP_ENV;
const developmentValues = require('./env/development').defaults;
const productionValues = require('./env/production').defaults;

const envValues = env === 'development' ? developmentValues : productionValues;
const isDevelopment = (envValues.NODE_ENV === 'development');
const isProduction = (envValues.NODE_ENV === 'production');

/**
 * `.pug`を`.html`にコンパイルします。
 * JSONとページごとのルート相対パスの格納、ルート相対パスを使ったincludeの設定をします。
 */
gulp.task('html', () => {
  // JSONファイルの読み込み。
  const locals = {
    site: JSON.parse(fs.readFileSync(`${src.data}site.json`)),
  };
  locals.ja = {
    // 日本語サイト共通のデータです。
    site: JSON.parse(fs.readFileSync(`${src.data}ja/site.json`)),
  };
  locals.en = {
    // 英語サイト共通のデータです。
    site: JSON.parse(fs.readFileSync(`${src.data}en/site.json`)),
  };
  return gulp.src(src.html)
  // エラーでタスクを止めない
    .pipe(plumber({ errorHandler: notify.onError('Error: <%= error.message %>') }))
    .pipe(data((file) => {
    // 各ページのルート相対パスを格納します。
      locals.pageAbsolutePath = `/${path.relative(file.base, file.path.replace(/.pug$/, '.html')).replace(/index\.html$/, '')}`;
      return locals;
    }))
    .pipe(cache('html'))
    .pipe(pug({
    // `locals`に渡したデータを各Pugファイルで取得できます。
      locals,
      // ルート相対パスでincludeが使えるようにします。
      basedir: 'src',
      // Pugファイルの整形。
      pretty: true,
    }))
    .pipe(gulp.dest(dest.root))
    .pipe(browserSync.reload({ stream: true }));
});

/**
 * 公開用のHTMLファイルを解析して警告やエラーを通知します。
 */
gulp.task('htmlhint', () => gulp.src([`${dest.root}**/*.html`, `!${dest.root}styleguide/**/*.html`])
  .pipe(htmlhint('.htmlhintrc'))
  .pipe(htmlhint.reporter('htmlhint-stylish'))
  .pipe(htmlhint.failOnError({
    suppress: true,
  })));

/**
 * /public/以下のHTMLファイルを監視、更新があれば反映します。
 */
gulp.task('ssi', () => gulp.src(src.ssi)
  .pipe(browserSync.reload({ stream: true })));

/**
 * 公開用のSassファイルを解析して警告やエラーを通知します。
 * 修正できるものは強制的に反映します。
 */
gulp.task('stylelint', () => gulp.src([src.css, '!src/assets/css/base/mixin/**/*.scss'])
  .pipe(gulpStylelint({
    fix: true,
  }))
  .pipe(gulp.dest(src.root)));

/**
 * `.scss`を`.css`にコンパイルします。
 */
gulp.task('css', () => {
  const plugins = [
    flexBugsFixes(),
    autoprefixer(),
  ];
  return gulp.src(src.css)
  // globパターンでのインポート機能を追加
    .pipe(sassGlob())
    .pipe(gulpif(isDevelopment, sourcemaps.init()))
    .pipe(sass({
      outputStyle: 'expanded',
    }).on('error', sass.logError))
    .pipe(plumber({ errorHandler: notify.onError('Error: <%= error.message %>') }))
    .pipe(postcss(plugins))
    .pipe(gulpif(isProduction, cleanCSS()))
    .pipe(gulpif(isDevelopment, sourcemaps.write()))
    .pipe(gulp.dest(dest.root))
    .pipe(browserSync.reload({ stream: true }));
});

/**
 * ES2015以降のコードをES5に変換（トランスコンパイル）します。
 */
function bundle(watching = false) {
  const envifyOptions = envValues;
  // 指定した環境変数だけを処理する
  envifyOptions._ = 'purge';

  const b = browserify({
    entries: src.js,
    transform: ['babelify'],
    // browserifyのsourcemapsを使用しない
    debug: (isDevelopment) ? true : false,
    cache: {},
    packageCache: {},
    // ファイルの状態を監視して、差分だけをビルドする
    plugin: (watching) ? [watchify] : null
  })
  // Vue.jsの単一ファイルコンポーネントをBrowserifyで変換する
  .transform(vueify)
  .transform(
    {global: true},
    // `NODE_ENV`を`development`か`production`に変更する
    // 環境変数もすべてJS側に渡す
    envify(envifyOptions)
  )
  .on('update', () => {
    bundler();
    console.log('scripts rebuild');
  });

  function bundler() {
    return b.bundle()
    .on('error', (err) => {
      console.log(err.message);
    })
    .pipe(source('site.js'))
    .pipe(buffer())
    .pipe(gulpif(isProduction, uglify({output: {comments: /^!/}})))
    .pipe(gulp.dest(dest.js))
    .pipe(browserSync.reload({stream: true}))
  }
  return bundler();
}

gulp.task('js', () => {
  bundle();
});

/**
 * 画像を圧縮します。
 */
gulp.task('image', () => gulp.src(src.image)
  .pipe(changed(dest.image))
  .pipe(plumber({
    errorHandler(err) {
      console.log(err.messageFormatted);
      this.emit('end');
    },
  }))
  .pipe(imagemin({
    // jpgをロスレス圧縮（画質を落とさず、メタデータを削除）。
    progressive: true,
    // gifをインターレースgifにします。
    interlaced: true,
    // PNGファイルの圧縮率（7が最高）を指定します。
    optimizationLevel: 7,
  }))
  .pipe(gulp.dest(dest.image))
  .pipe(browserSync.reload({ stream: true })));

/**
 * SVGファイルからSVGスプライトを生成します。
 */
gulp.task('svgSprite', () => gulp.src(src.svgSprite)
  .pipe(plumber({ errorHandler: notify.onError('Error: <%= error.message %>') }))
  .pipe(svgSprite({
    mode: {
      // SVGファイルをsymbol要素としてまとめる。
      symbol: {
        dest: './',
        // 出力するファイル名。
        sprite: 'sprite.svg',
      },
    },
    shape: {
      transform: [{
        svgo: {
          plugins: [
            // `title`タグを削除する。
            { removeTitle: true },
            // `style`属性を削除する。
            { removeStyleElement: true },
            // `fill`属性を削除して、CSSで`fill`の変更ができるようにする。
            { removeAttrs: { attrs: 'fill' } },
          ],
        },
      }],
    },
    svg: {
      // xml宣言を出力する。
      xmlDeclaration: true,
      // DOCTYPE宣言を出力する。
      doctypeDeclaration: false,
    },
  }))
  .pipe(gulp.dest(dest.svgSprite))
  .pipe(browserSync.reload({ stream: true })));

/**
 * スタイルガイドを生成します。
 */
gulp.task('styleguide', () => gulp.src('./aigis/aigis_config.yml')
  .pipe(aigis()));

/**
 * Gulpの処理を通さないディレクトリです。
 * 公開用のディレクトリにコピーします。
 */
gulp.task('public', () => gulp.src(src.public)
  .pipe(gulp.dest(dest.root)));

/**
 * 公開用のディレクトリを削除します。
 */
gulp.task('clean:dest', cb => rimraf(dest.root, cb));

/**
 * 一連のタスクを処理します。
 */
gulp.task('build', () => {
  runSequence(
    ['html', 'ssi', 'css', 'styleguide', 'js', 'image', 'svgSprite', 'public'],
  );
});

/**
 * ローカルサーバーを起動します。
 */
gulp.task('browser-sync', () => {
  browserSync({
    server: {
      // SSIを使用します。
      middleware: [
        ssi({
          baseDir: dest.root,
          ext: '.html',
        }),
      ],
      baseDir: dest.root,
    },
    // 画面を共有するときにスクロールやクリックなどをミラーリングしたくない場合はfalseにします。
    ghostMode: false,
    // ローカルIPアドレスでサーバーを立ち上げます。
    open: 'external',
    // サーバー起動時に表示するページを指定します。
    startPath: '/styleguide/',
    // falseに指定すると、サーバー起動時にポップアップを表示させません。
    notify: false,
  });
});

/**
 * ファイルを監視します。
 */
gulp.task('watch', ['build'], () => {
  gulp.watch(src.ssi, ['ssi']);
  gulp.watch(src.public, ['public']);
  gulp.watch(src.html, ['html']);
  gulp.watch(src.css, ['css']);
  gulp.watch(src.styleguideWatch, ['styleguide']);
  bundle(true);
  gulp.watch(src.imageWatch, ['image']);
  gulp.watch(src.svgSprite, ['svgSprite']);
});

/**
 * 開発に使用するタスクです。
 * `gulp`タスクにbrowser-syncを追加します。
 * ローカルサーバーを起動し、リアルタイムに更新を反映させます。
 */
gulp.task('default', ['clean:dest'], () => {
  runSequence(
    'watch',
    'browser-sync',
  );
});
<commit_msg>Improve: JSのタスクを見直し<commit_after>const gulp = require('gulp');

// Pug
const pug = require('gulp-pug');
const fs = require('fs');
const data = require('gulp-data');
const path = require('path');

// HTML
const htmlhint = require('gulp-htmlhint');
const ssi = require('browsersync-ssi');

// CSS
const sass = require('gulp-sass');
const sassGlob = require('gulp-sass-glob');
const postcss = require('gulp-postcss');
const flexBugsFixes = require('postcss-flexbugs-fixes');
const autoprefixer = require('autoprefixer');
const cleanCSS = require('gulp-clean-css');
const gulpStylelint = require('gulp-stylelint');

// JS
const uglify = require('gulp-uglify');
const browserify = require('browserify');
const babelify = require('babelify');
const source = require('vinyl-source-stream');
const buffer = require('vinyl-buffer');
const vueify = require('vueify');
const envify = require('envify/custom');

// Image
const imagemin = require('gulp-imagemin');

// SVG sprite
const svgSprite = require('gulp-svg-sprite');

// Styleguide
const aigis = require('gulp-aigis');

// Utility
const cache = require('gulp-cached');
const changed = require('gulp-changed');
const sourcemaps = require('gulp-sourcemaps');
const runSequence = require('run-sequence');
const browserSync = require('browser-sync');
const plumber = require('gulp-plumber');
const notify = require('gulp-notify');
const gulpif = require('gulp-if');
const rimraf = require('rimraf');

/**
 * 開発用ディレクトリ
 */
const src = {
  root: 'src/',
  html: ['src/**/*.pug', '!src/**/_*.pug'],
  ssi: 'public/**/*.html',
  data: 'src/_data/',
  css: 'src/**/*.scss',
  styleguideWatch: ['src/**/*.scss', 'src/**/*.md'],
  js: 'src/assets/js/site.js',
  image: 'src/assets/img/**/*.{png,jpg,gif,svg}',
  imageWatch: 'src/assets/img/**/*',
  svgSprite: 'src/assets/svg/**/*.svg',
  public: 'public/**/*',
};

/**
 * テスト用ディレクトリ
 */
const dest = {
  root: 'htdocs/',
  image: 'htdocs/assets/img/',
  js: 'htdocs/assets/js/',
  svgSprite: 'htdocs/assets/svg/',
};

/**
 * 環境変数を設定します。
 */
const env = process.env.APP_ENV;
const developmentValues = require('./env/development').defaults;
const productionValues = require('./env/production').defaults;

const envValues = env === 'development' ? developmentValues : productionValues;
const isDevelopment = (envValues.NODE_ENV === 'development');
const isProduction = (envValues.NODE_ENV === 'production');

/**
 * `.pug`を`.html`にコンパイルします。
 * JSONとページごとのルート相対パスの格納、ルート相対パスを使ったincludeの設定をします。
 */
gulp.task('html', () => {
  // JSONファイルの読み込み。
  const locals = {
    site: JSON.parse(fs.readFileSync(`${src.data}site.json`)),
  };
  locals.ja = {
    // 日本語サイト共通のデータです。
    site: JSON.parse(fs.readFileSync(`${src.data}ja/site.json`)),
  };
  locals.en = {
    // 英語サイト共通のデータです。
    site: JSON.parse(fs.readFileSync(`${src.data}en/site.json`)),
  };
  return gulp.src(src.html)
  // エラーでタスクを止めない
    .pipe(plumber({ errorHandler: notify.onError('Error: <%= error.message %>') }))
    .pipe(data((file) => {
    // 各ページのルート相対パスを格納します。
      locals.pageAbsolutePath = `/${path.relative(file.base, file.path.replace(/.pug$/, '.html')).replace(/index\.html$/, '')}`;
      return locals;
    }))
    .pipe(cache('html'))
    .pipe(pug({
    // `locals`に渡したデータを各Pugファイルで取得できます。
      locals,
      // ルート相対パスでincludeが使えるようにします。
      basedir: 'src',
      // Pugファイルの整形。
      pretty: true,
    }))
    .pipe(gulp.dest(dest.root))
    .pipe(browserSync.reload({ stream: true }));
});

/**
 * 公開用のHTMLファイルを解析して警告やエラーを通知します。
 */
gulp.task('htmlhint', () => gulp.src([`${dest.root}**/*.html`, `!${dest.root}styleguide/**/*.html`])
  .pipe(htmlhint('.htmlhintrc'))
  .pipe(htmlhint.reporter('htmlhint-stylish'))
  .pipe(htmlhint.failOnError({
    suppress: true,
  })));

/**
 * /public/以下のHTMLファイルを監視、更新があれば反映します。
 */
gulp.task('ssi', () => gulp.src(src.ssi)
  .pipe(browserSync.reload({ stream: true })));

/**
 * 公開用のSassファイルを解析して警告やエラーを通知します。
 * 修正できるものは強制的に反映します。
 */
gulp.task('stylelint', () => gulp.src([src.css, '!src/assets/css/base/mixin/**/*.scss'])
  .pipe(gulpStylelint({
    fix: true,
  }))
  .pipe(gulp.dest(src.root)));

/**
 * `.scss`を`.css`にコンパイルします。
 */
gulp.task('css', () => {
  const plugins = [
    flexBugsFixes(),
    autoprefixer(),
  ];
  return gulp.src(src.css)
  // globパターンでのインポート機能を追加
    .pipe(sassGlob())
    .pipe(gulpif(isDevelopment, sourcemaps.init()))
    .pipe(sass({
      outputStyle: 'expanded',
    }).on('error', sass.logError))
    .pipe(plumber({ errorHandler: notify.onError('Error: <%= error.message %>') }))
    .pipe(postcss(plugins))
    .pipe(gulpif(isProduction, cleanCSS()))
    .pipe(gulpif(isDevelopment, sourcemaps.write()))
    .pipe(gulp.dest(dest.root))
    .pipe(browserSync.reload({ stream: true }));
});

/**
 * ES2015以降のコードをES5に変換（トランスコンパイル）します。
 */
gulp.task('js', () => {
  const envifyOptions = envValues;
  // 指定した環境変数だけを処理する
  envifyOptions._ = 'purge';

  browserify({
    entries: src.js,
    extensions: ['.js'],
    // browserifyのsourcemapsを使用しない
    debug: !!(isDevelopment),
    cache: {},
    packageCache: {},
    // ファイルの状態を監視して、差分だけをビルドする
    plugin: 'watchify',
  })
    .transform(babelify)
    // Vue.jsの単一ファイルコンポーネントをBrowserifyで変換する
    .transform(vueify)
    .transform(
      { global: true },
      // `NODE_ENV`を`development`か`production`に変更する
      // 環境変数もすべてJS側に渡す
      envify(envifyOptions),
    )
    .bundle()
    .on('error', function (err) {
      console.log(`Error : ${err.message}`);
      this.emit('end');
    })
    .pipe(source('site.js'))
    .pipe(buffer())
    .pipe(gulpif(isProduction, uglify({ output: { comments: /^!/ } })))
    .pipe(gulp.dest(dest.js))
    .pipe(browserSync.reload({ stream: true }));
});

/**
 * 画像を圧縮します。
 */
gulp.task('image', () => gulp.src(src.image)
  .pipe(changed(dest.image))
  .pipe(plumber({
    errorHandler(err) {
      console.log(err.messageFormatted);
      this.emit('end');
    },
  }))
  .pipe(imagemin({
    // jpgをロスレス圧縮（画質を落とさず、メタデータを削除）。
    progressive: true,
    // gifをインターレースgifにします。
    interlaced: true,
    // PNGファイルの圧縮率（7が最高）を指定します。
    optimizationLevel: 7,
  }))
  .pipe(gulp.dest(dest.image))
  .pipe(browserSync.reload({ stream: true })));

/**
 * SVGファイルからSVGスプライトを生成します。
 */
gulp.task('svgSprite', () => gulp.src(src.svgSprite)
  .pipe(plumber({ errorHandler: notify.onError('Error: <%= error.message %>') }))
  .pipe(svgSprite({
    mode: {
      // SVGファイルをsymbol要素としてまとめる。
      symbol: {
        dest: './',
        // 出力するファイル名。
        sprite: 'sprite.svg',
      },
    },
    shape: {
      transform: [{
        svgo: {
          plugins: [
            // `title`タグを削除する。
            { removeTitle: true },
            // `style`属性を削除する。
            { removeStyleElement: true },
            // `fill`属性を削除して、CSSで`fill`の変更ができるようにする。
            { removeAttrs: { attrs: 'fill' } },
          ],
        },
      }],
    },
    svg: {
      // xml宣言を出力する。
      xmlDeclaration: true,
      // DOCTYPE宣言を出力する。
      doctypeDeclaration: false,
    },
  }))
  .pipe(gulp.dest(dest.svgSprite))
  .pipe(browserSync.reload({ stream: true })));

/**
 * スタイルガイドを生成します。
 */
gulp.task('styleguide', () => gulp.src('./aigis/aigis_config.yml')
  .pipe(aigis()));

/**
 * Gulpの処理を通さないディレクトリです。
 * 公開用のディレクトリにコピーします。
 */
gulp.task('public', () => gulp.src(src.public)
  .pipe(gulp.dest(dest.root)));

/**
 * 公開用のディレクトリを削除します。
 */
gulp.task('clean:dest', cb => rimraf(dest.root, cb));

/**
 * 一連のタスクを処理します。
 */
gulp.task('build', () => {
  runSequence(
    ['html', 'ssi', 'css', 'styleguide', 'js', 'image', 'svgSprite', 'public'],
  );
});

/**
 * ローカルサーバーを起動します。
 */
gulp.task('browser-sync', () => {
  browserSync({
    server: {
      // SSIを使用します。
      middleware: [
        ssi({
          baseDir: dest.root,
          ext: '.html',
        }),
      ],
      baseDir: dest.root,
    },
    // 画面を共有するときにスクロールやクリックなどをミラーリングしたくない場合はfalseにします。
    ghostMode: false,
    // ローカルIPアドレスでサーバーを立ち上げます。
    open: 'external',
    // サーバー起動時に表示するページを指定します。
    startPath: '/styleguide/',
    // falseに指定すると、サーバー起動時にポップアップを表示させません。
    notify: false,
  });
});

/**
 * ファイルを監視します。
 */
gulp.task('watch', ['build'], () => {
  gulp.watch(src.ssi, ['ssi']);
  gulp.watch(src.public, ['public']);
  gulp.watch(src.html, ['html']);
  gulp.watch(src.css, ['css']);
  gulp.watch(src.styleguideWatch, ['styleguide']);
  gulp.watch(src.js, ['js']);
  gulp.watch(src.imageWatch, ['image']);
  gulp.watch(src.svgSprite, ['svgSprite']);
});

/**
 * 開発に使用するタスクです。
 * `gulp`タスクにbrowser-syncを追加します。
 * ローカルサーバーを起動し、リアルタイムに更新を反映させます。
 */
gulp.task('default', ['clean:dest'], () => {
  runSequence(
    'watch',
    'browser-sync',
  );
});
<|endoftext|>


<commit_before>    //
    describe("Test extension object", function() {

        it("Check basic properties", function() {
            expect(typeof(require)).toBe('object');
            expect(typeof(require.paths)).toBe('object');
        });
    });<commit_msg>removed master<commit_after>    //
    describe("Test require object", function() {

        it("Check basic properties", function() {
            expect(typeof(require)).toBe('object');
            expect(typeof(require.paths)).toBe('object');
            expect(typeof(require.shim)).toBe('object');
            expect(require.deps instanceof Array).toBe(true);
            expect(typeof(require.buildUrl)).toBe('string');
        });
    });<|endoftext|>


<commit_before>/*
 * Room Settings plugin
 * By: panpawn
 * Special thanks: sirDonovan, Slayer95, Zarel
 */

'use strict';

const RANKS = Config.groupsranking;
const DISABLED = 'disabled="" style="font-weight:bold;"';

const SLOWCHAT_USER_REQUIREMENT = 10;

class RoomSettings {
	constructor(user, room, connection) {
		this.room = room;
		this.user = user;
		this.connection = connection;
		this.sameCommand = true;
	}
	updateSetting(command) {
		CommandParser.parse('/' + command, this.room, this.user, this.connection);
		this.sameCommand = false;
		this.generateDisplay();
	}
	modchat() {
		if (!this.user.can('modchat', null, this.room)) return "<button " + DISABLED + ">" + (this.room.modchat ? this.room.modchat : false) + "</button>";
		let modchatOutput = [];
		for (let i = 0; i <= RANKS.length; i++) {
			if (RANKS[i] === ' ' && !this.room.modchat) {
				modchatOutput.push('<button ' + DISABLED + '>false</button>');
			} else if (RANKS[i] === ' ') {
				modchatOutput.push('<button name="send" value="/roomsetting modchat false">false</button>');
			} else if (RANKS[i] === this.room.modchat) {
				modchatOutput.push('<button ' + DISABLED + '>' + RANKS[i] + '</button>');
			} else if (RANKS[i]) {
				let rankIndex = RANKS.indexOf(RANKS[i]);
				let roomAuth = (this.room.auth && this.room.auth[this.user.userid] ? this.room.auth[this.user.userid] : false);
				let roomAtuhIndex = (roomAuth ? RANKS.indexOf(roomAuth) : false);
				if (rankIndex > 1 && !this.user.can('modchatall', null, this.room)) continue;
				if (roomAuth && !this.user.can('bypassall')) {
					if (rankIndex > roomAtuhIndex) continue;
				}
				modchatOutput.push('<button name="send" value="/roomsetting modchat ' + RANKS[i] + '">' + RANKS[i] + '</button>');
			}
		}
		// Since autoconfirmed isn't technically a Config rank...
		modchatOutput.splice(1, 0, '<button ' + (this.room.modchat !== 'autoconfirmed' ? 'name="send" value="/roomsetting modchat autoconfirmed"' : DISABLED) + '>AC</button>');
		return modchatOutput.join(" | ");
	}
	modjoin() {
		if (!this.user.can('makeroom') && !this.room.isPersonal) return "<button " + DISABLED + ">" + (this.room.modjoin ? this.room.modjoin : false) + "</button>";
		let modjoinOutput = [];
		for (let i = 0; i < RANKS.length; i++) {
			if (RANKS[i] === ' ' && !this.room.modjoin) {
				modjoinOutput.push('<button ' + DISABLED + '>false</button>');
			} else if (RANKS[i] === ' ') {
				modjoinOutput.push('<button name="send" value="/roomsetting modjoin false">false</button>');
			} else if (RANKS[i] === this.room.modjoin) {
				modjoinOutput.push('<button ' + DISABLED + '>' + RANKS[i] + '</button>');
			} else if (RANKS[i]) {
				// Personal rooms modjoin check
				if (this.room.isPersonal && !this.user.can('makeroom') && RANKS[i] !== '+') continue;

				modjoinOutput.push('<button name="send" value="/roomsetting modjoin ' + RANKS[i] + '">' + RANKS[i] + '</button>');
			}
		}
		return modjoinOutput.join(" | ");
	}
	stretching() {
		if (!this.user.can('editroom', null, this.room)) return "<button " + DISABLED + ">" + (this.room.stretching ? this.room.stretching : false) + "</button>";
		if (this.room.filterStretching) {
			return '<button name="send" value="/roomsetting stretching disable">false</button> | <button ' + DISABLED + '>true</button>';
		} else {
			return '<button ' + DISABLED + '>false</button> | <button name="send" value="/roomsetting stretching enable">true</button>';
		}
	}
	capitals() {
		if (!this.user.can('editroom', null, this.room)) return "<button " + DISABLED + ">" + (this.room.capitals ? this.room.capitals : false) + "</button>";
		if (this.room.filterCaps) {
			return '<button name="send" value="/roomsetting capitals disable">false</button> | <button ' + DISABLED + '>true</button>';
		} else {
			return '<button ' + DISABLED + '>false</button> | <button name="send" value="/roomsetting capitals enable">true</button>';
		}
	}
	slowchat() {
		if (!this.user.can('editroom', null, this.room) || this.room.userCount < SLOWCHAT_USER_REQUIREMENT) return "<button " + DISABLED + ">" + (this.room.slowchat ? this.room.slowchat : false) + "</button>";

		let slowchatOutput = [];
		for (let i = 10; i <= 60; i += 10) {
			if (this.room.slowchat === i) {
				slowchatOutput.push('<button ' + DISABLED + '>' + i + '</button>');
			} else {
				slowchatOutput.push('<button name="send" value="/roomsetting slowchat ' + i + '">' + i + '</button>');
			}
		}
		if (!this.room.slowchat) {
			slowchatOutput.unshift('<button ' + DISABLED + '>false</button>');
		} else {
			slowchatOutput.unshift('<button name="send" value="/roomsettings slowchat false">false</button>');
		}
		return slowchatOutput.join(" | ");
	}
	generateDisplay(user, room, connection) {
		let output = '
Room Settings for ' + Tools.escapeHTML(this.room.title) + '
';
		output += "Modchat: " + this.modchat() + "
";
		output += "Modjoin: " + this.modjoin() + "
";
		output += "Stretching: " + this.stretching() + "
";
		output += "Capitals: " + this.capitals() + "
";
		output += "Slowchat: " + this.slowchat() + "
";
		output += "
";

		this.user.sendTo(this.room, '|uhtml' + (this.sameCommand ? '' : 'change') + '|roomsettings|' + output);
	}
}

exports.commands = {
	roomsetting: 'roomsettings',
	roomsettings: function (target, room, user, connection) {
		if (room.battle) return this.errorReply("This command cannot be used in battle rooms.");
		let settings = new RoomSettings(user, room, connection);

		if (!target) {
			room.update();
			settings.generateDisplay(user, room, connection);
		} else {
			settings.updateSetting(target);
		}
	},
	roomsettingshelp: ["/roomsettings - Shows current rom settings with buttons to change them. Requires # & ~"],
};
<commit_msg>Roomsettings: Update help command (#2680)<commit_after>/*
 * Room Settings plugin
 * By: panpawn
 * Special thanks: sirDonovan, Slayer95, Zarel
 */

'use strict';

const RANKS = Config.groupsranking;
const DISABLED = 'disabled="" style="font-weight:bold;"';

const SLOWCHAT_USER_REQUIREMENT = 10;

class RoomSettings {
	constructor(user, room, connection) {
		this.room = room;
		this.user = user;
		this.connection = connection;
		this.sameCommand = true;
	}
	updateSetting(command) {
		CommandParser.parse('/' + command, this.room, this.user, this.connection);
		this.sameCommand = false;
		this.generateDisplay();
	}
	modchat() {
		if (!this.user.can('modchat', null, this.room)) return "<button " + DISABLED + ">" + (this.room.modchat ? this.room.modchat : false) + "</button>";
		let modchatOutput = [];
		for (let i = 0; i <= RANKS.length; i++) {
			if (RANKS[i] === ' ' && !this.room.modchat) {
				modchatOutput.push('<button ' + DISABLED + '>false</button>');
			} else if (RANKS[i] === ' ') {
				modchatOutput.push('<button name="send" value="/roomsetting modchat false">false</button>');
			} else if (RANKS[i] === this.room.modchat) {
				modchatOutput.push('<button ' + DISABLED + '>' + RANKS[i] + '</button>');
			} else if (RANKS[i]) {
				let rankIndex = RANKS.indexOf(RANKS[i]);
				let roomAuth = (this.room.auth && this.room.auth[this.user.userid] ? this.room.auth[this.user.userid] : false);
				let roomAtuhIndex = (roomAuth ? RANKS.indexOf(roomAuth) : false);
				if (rankIndex > 1 && !this.user.can('modchatall', null, this.room)) continue;
				if (roomAuth && !this.user.can('bypassall')) {
					if (rankIndex > roomAtuhIndex) continue;
				}
				modchatOutput.push('<button name="send" value="/roomsetting modchat ' + RANKS[i] + '">' + RANKS[i] + '</button>');
			}
		}
		// Since autoconfirmed isn't technically a Config rank...
		modchatOutput.splice(1, 0, '<button ' + (this.room.modchat !== 'autoconfirmed' ? 'name="send" value="/roomsetting modchat autoconfirmed"' : DISABLED) + '>AC</button>');
		return modchatOutput.join(" | ");
	}
	modjoin() {
		if (!this.user.can('makeroom') && !this.room.isPersonal) return "<button " + DISABLED + ">" + (this.room.modjoin ? this.room.modjoin : false) + "</button>";
		let modjoinOutput = [];
		for (let i = 0; i < RANKS.length; i++) {
			if (RANKS[i] === ' ' && !this.room.modjoin) {
				modjoinOutput.push('<button ' + DISABLED + '>false</button>');
			} else if (RANKS[i] === ' ') {
				modjoinOutput.push('<button name="send" value="/roomsetting modjoin false">false</button>');
			} else if (RANKS[i] === this.room.modjoin) {
				modjoinOutput.push('<button ' + DISABLED + '>' + RANKS[i] + '</button>');
			} else if (RANKS[i]) {
				// Personal rooms modjoin check
				if (this.room.isPersonal && !this.user.can('makeroom') && RANKS[i] !== '+') continue;

				modjoinOutput.push('<button name="send" value="/roomsetting modjoin ' + RANKS[i] + '">' + RANKS[i] + '</button>');
			}
		}
		return modjoinOutput.join(" | ");
	}
	stretching() {
		if (!this.user.can('editroom', null, this.room)) return "<button " + DISABLED + ">" + (this.room.stretching ? this.room.stretching : false) + "</button>";
		if (this.room.filterStretching) {
			return '<button name="send" value="/roomsetting stretching disable">false</button> | <button ' + DISABLED + '>true</button>';
		} else {
			return '<button ' + DISABLED + '>false</button> | <button name="send" value="/roomsetting stretching enable">true</button>';
		}
	}
	capitals() {
		if (!this.user.can('editroom', null, this.room)) return "<button " + DISABLED + ">" + (this.room.capitals ? this.room.capitals : false) + "</button>";
		if (this.room.filterCaps) {
			return '<button name="send" value="/roomsetting capitals disable">false</button> | <button ' + DISABLED + '>true</button>';
		} else {
			return '<button ' + DISABLED + '>false</button> | <button name="send" value="/roomsetting capitals enable">true</button>';
		}
	}
	slowchat() {
		if (!this.user.can('editroom', null, this.room) || this.room.userCount < SLOWCHAT_USER_REQUIREMENT) return "<button " + DISABLED + ">" + (this.room.slowchat ? this.room.slowchat : false) + "</button>";

		let slowchatOutput = [];
		for (let i = 10; i <= 60; i += 10) {
			if (this.room.slowchat === i) {
				slowchatOutput.push('<button ' + DISABLED + '>' + i + '</button>');
			} else {
				slowchatOutput.push('<button name="send" value="/roomsetting slowchat ' + i + '">' + i + '</button>');
			}
		}
		if (!this.room.slowchat) {
			slowchatOutput.unshift('<button ' + DISABLED + '>false</button>');
		} else {
			slowchatOutput.unshift('<button name="send" value="/roomsettings slowchat false">false</button>');
		}
		return slowchatOutput.join(" | ");
	}
	generateDisplay(user, room, connection) {
		let output = '
Room Settings for ' + Tools.escapeHTML(this.room.title) + '
';
		output += "Modchat: " + this.modchat() + "
";
		output += "Modjoin: " + this.modjoin() + "
";
		output += "Stretching: " + this.stretching() + "
";
		output += "Capitals: " + this.capitals() + "
";
		output += "Slowchat: " + this.slowchat() + "
";
		output += "
";

		this.user.sendTo(this.room, '|uhtml' + (this.sameCommand ? '' : 'change') + '|roomsettings|' + output);
	}
}

exports.commands = {
	roomsetting: 'roomsettings',
	roomsettings: function (target, room, user, connection) {
		if (room.battle) return this.errorReply("This command cannot be used in battle rooms.");
		let settings = new RoomSettings(user, room, connection);

		if (!target) {
			room.update();
			settings.generateDisplay(user, room, connection);
		} else {
			settings.updateSetting(target);
		}
	},
	roomsettingshelp: ["/roomsettings - Shows current room settings with buttons to change them (if you can)."],
};
<|endoftext|>


<commit_before>/**
 * Story
 *
 * @module      ::  Model
 * @description ::  This model represent user story on taskboard. User stories are attached to project and
 *                  sprint. Sprint relation is not required, if sprintId is 0 or null current user story is
 *                  in project backlog.
 */
var moment = require("moment-timezone");

module.exports = {
    schema: true,
    attributes: {
        // Relation to Project model
        projectId: {
            type:       'integer',
            required:   true
        },
        // Relation to Sprint model
        sprintId: {
            type:       'integer',
            defaultsTo: 0
        },
        // Relation to Milestone model
        milestoneId: {
            type:       'integer',
            defaultsTo: 0
        },
        // Relation to type model, note that this is just default type for story tasks
        typeId: {
            type:       'integer',
            defaultsTo: 0,
            required:   true
        },
        title: {
            type:       'string',
            required:   true,
            minLength:  5
        },
        description: {
            type:       'text',
            required:   true,
            minLength:  10
        },
        estimate: {
            type:       'integer',
            required:   true,
            defaultsTo: -1
        },
        priority: {
            type:       'integer',
            defaultsTo: 0
        },
        vfCase: {
            type:       'integer',
            defaultsTo: 0
        },
        isDone: {
            type:       'boolean',
            required:   true,
            defaultsTo: 0
        },
        timeStart: {
            type:       'datetime'
        },
        timeEnd: {
            type:       'datetime'
        },

        estimateFormatted: function() {
            return (parseInt(this.estimate, 10) === -1) ? '???' : this.estimate;
        },
        objectTitle: function() {
            return this.title;
        },
        createdAtObject: function() {
            return (this.createdAt && this.createdAt != '0000-00-00')
                ? DateService.convertDateObjectToUtc(this.createdAt) : null;
        },
        updatedAtObject: function() {
            return (this.updatedAt && this.updatedAt != '0000-00-00')
                ? DateService.convertDateObjectToUtc(this.updatedAt) : null;
        },
        timeStartObject: function() {
            return (this.timeStart && this.timeStart != '0000-00-00 00:00:00')
                ? DateService.convertDateObjectToUtc(this.timeStart) : null;
        },
        timeEndObject: function() {
            return (this.timeStart && this.timeStart != '0000-00-00 00:00:00')
                ? DateService.convertDateObjectToUtc(this.timeEnd) : null;
        },
        timeDuration: function() {
            var output;

            if (moment.isMoment(this.timeStartObject()) && moment.isMoment(this.timeEndObject())) {
                output = this.timeEndObject().diff(this.timeStartObject(), 'seconds');
            } else {
                output = 0;
            }

            return output;
        },
        timeDurationHuman: function() {
            var output;

            if (moment.isMoment(this.timeStartObject()) && moment.isMoment(this.timeEndObject())) {
                output = this.timeStartObject().from(this.timeEndObject(), true);
            } else {
                output = "";
            }

            return output;
        }
    },

    // Lifecycle Callbacks

    /**
     * Before create callback. Basically we want to make sure that isDone bit is set to false
     * and calculate story priority according to same project and sprint stories.
     *
     * @param   {sails.model.story} values
     * @param   {Function}          cb
     */
    beforeCreate: function(values, cb) {
        values.isDone = false;

        Story
            .findOne({
                projectId: values.projectId,
                sprintId: values.sprintId
            })
            .sort('priority DESC')
            .done(function(error, story) {
                var priority;

                if (error) {
                    cb(error)
                } else if (!story) {
                    priority = 0;
                } else {
                    priority = story.priority + 1;
                }

                values.priority = priority;

                cb();
            });
    },

    /**
     * After create callback
     *
     * @param   {sails.model.story} values
     * @param   {Function}          cb
     */
    afterCreate: function(values, cb) {
        HistoryService.write('Story', values);

        cb();
    },

    /**
     * After update callback
     *
     * @param   {sails.model.story} values
     * @param   {Function}          cb
     */
    afterUpdate: function(values, cb) {
        HistoryService.write('Story', values);

        cb();
    },

    /**
     * Before destroy callback
     *
     * @param   {Object}    terms
     * @param   {Function}  cb
     */
    beforeDestroy: function(terms, cb) {
        Story
            .findOne(terms)
            .done(function(error, story) {
                HistoryService.remove('Story', story.id);

                cb();
            });
    }
};
<commit_msg>Bug fix.<commit_after>/**
 * Story
 *
 * @module      ::  Model
 * @description ::  This model represent user story on taskboard. User stories are attached to project and
 *                  sprint. Sprint relation is not required, if sprintId is 0 or null current user story is
 *                  in project backlog.
 */
var moment = require("moment-timezone");

module.exports = {
    schema: true,
    attributes: {
        // Relation to Project model
        projectId: {
            type:       'integer',
            required:   true
        },
        // Relation to Sprint model
        sprintId: {
            type:       'integer',
            defaultsTo: 0
        },
        // Relation to Milestone model
        milestoneId: {
            type:       'integer',
            defaultsTo: 0
        },
        // Relation to type model, note that this is just default type for story tasks
        typeId: {
            type:       'integer',
            defaultsTo: 0,
            required:   true
        },
        title: {
            type:       'string',
            required:   true,
            minLength:  5
        },
        description: {
            type:       'text',
            required:   true,
            minLength:  10
        },
        estimate: {
            type:       'integer',
            required:   true,
            defaultsTo: -1
        },
        priority: {
            type:       'integer',
            defaultsTo: 0
        },
        vfCase: {
            type:       'integer',
            defaultsTo: 0
        },
        isDone: {
            type:       'boolean',
            required:   true,
            defaultsTo: 0
        },
        timeStart: {
            type:       'datetime'
        },
        timeEnd: {
            type:       'datetime'
        },

        estimateFormatted: function() {
            return (parseInt(this.estimate, 10) === -1) ? '???' : this.estimate;
        },
        objectTitle: function() {
            return this.title;
        },
        createdAtObject: function() {
            return (this.createdAt && this.createdAt != '0000-00-00')
                ? DateService.convertDateObjectToUtc(this.createdAt) : null;
        },
        updatedAtObject: function() {
            return (this.updatedAt && this.updatedAt != '0000-00-00')
                ? DateService.convertDateObjectToUtc(this.updatedAt) : null;
        },
        timeStartObject: function() {
            return (this.timeStart && this.timeStart != '0000-00-00 00:00:00')
                ? DateService.convertDateObjectToUtc(this.timeStart) : null;
        },
        timeEndObject: function() {
            return (this.timeEnd && this.timeEnd != '0000-00-00 00:00:00')
                ? DateService.convertDateObjectToUtc(this.timeEnd) : null;
        },
        timeDuration: function() {
            var output;

            if (moment.isMoment(this.timeStartObject()) && moment.isMoment(this.timeEndObject())) {
                output = this.timeEndObject().diff(this.timeStartObject(), 'seconds');
            } else {
                output = 0;
            }

            return output;
        },
        timeDurationHuman: function() {
            var output;

            if (moment.isMoment(this.timeStartObject()) && moment.isMoment(this.timeEndObject())) {
                output = this.timeStartObject().from(this.timeEndObject(), true);
            } else {
                output = "";
            }

            return output;
        }
    },

    // Lifecycle Callbacks

    /**
     * Before create callback. Basically we want to make sure that isDone bit is set to false
     * and calculate story priority according to same project and sprint stories.
     *
     * @param   {sails.model.story} values
     * @param   {Function}          cb
     */
    beforeCreate: function(values, cb) {
        values.isDone = false;

        Story
            .findOne({
                projectId: values.projectId,
                sprintId: values.sprintId
            })
            .sort('priority DESC')
            .done(function(error, story) {
                var priority;

                if (error) {
                    cb(error)
                } else if (!story) {
                    priority = 0;
                } else {
                    priority = story.priority + 1;
                }

                values.priority = priority;

                cb();
            });
    },

    /**
     * After create callback
     *
     * @param   {sails.model.story} values
     * @param   {Function}          cb
     */
    afterCreate: function(values, cb) {
        HistoryService.write('Story', values);

        cb();
    },

    /**
     * After update callback
     *
     * @param   {sails.model.story} values
     * @param   {Function}          cb
     */
    afterUpdate: function(values, cb) {
        HistoryService.write('Story', values);

        cb();
    },

    /**
     * Before destroy callback
     *
     * @param   {Object}    terms
     * @param   {Function}  cb
     */
    beforeDestroy: function(terms, cb) {
        Story
            .findOne(terms)
            .done(function(error, story) {
                HistoryService.remove('Story', story.id);

                cb();
            });
    }
};
<|endoftext|>


<commit_before>'use strict';

require('../setup');

var _ = require('underscore');
var assert = require('assert');
var PSQL = require('../../lib/psql');
var pg = require('pg');

var dbopts_auth = {
    host: global.settings.db_host,
    port: global.settings.db_port,
    user: _.template(global.settings.db_user, {user_id: 1}),
    dbname: _.template(global.settings.db_base_name, {user_id: 1}),
    pass: _.template(global.settings.db_user_pass, {user_id: 1})
};

var POOL_PARAMS = {};

var TRANSACTION_ABORTED_ERR_MESSAGE = 'current transaction is aborted, commands ignored until end of transaction block';

describe('transaction', function() {

    beforeEach(function() {
        pg.pools.all = {};
    });

    var querySuite = [
        {
            destroyOnError: true,
            assertFn: function validate(done) {
                return function(err, result) {
                    assert.ok(result);
                    assert.equal(result.rows[0].foo, 1);
                    done();
                };
            }
        },
        {
            destroyOnError: false,
            assertFn: function validate(done) {
                return function(err) {
                    assert.ok(err);
                    assert.equal(err.message, TRANSACTION_ABORTED_ERR_MESSAGE);
                    done();
                };
            }
        }
    ];

    querySuite.forEach(function(scenario) {
        var shouldOrShouldNot = scenario.destroyOnError ? ' ' : ' NOT ';
        it('should' + shouldOrShouldNot + 'run query after transaction fails', function(done) {
            var psql = new PSQL(dbopts_auth, POOL_PARAMS, { destroyOnError: scenario.destroyOnError });
            var sql = "BEGIN; select error; COMMIT;";
            psql.query(sql, function(err) {
                assert.ok(err);
                assert.equal(err.message, 'column "error" does not exist');
                psql.query('select 1 as foo', scenario.assertFn(done));
            });
        });
    });

    var eventedQuerySuite = [
        {
            destroyOnError: true,
            assertFn: function validate(done) {
                return function(err, query) {
                    query.on('row', function(row) {
                        assert.ok(row);
                        assert.equal(row.foo, 1);
                        done();
                    });
                };
            }
        },
        {
            destroyOnError: false,
            assertFn: function validate(done) {
                return function(err, query) {
                    query.on('error', function(err) {
                        assert.ok(err);
                        assert.equal(err.message, TRANSACTION_ABORTED_ERR_MESSAGE);
                        done();
                    });
                };
            }
        }
    ];

    eventedQuerySuite.forEach(function(scenario) {
        var shouldOrShouldNot = scenario.destroyOnError ? ' ' : ' NOT ';
        it('should' + shouldOrShouldNot + 'run evented query after transaction fails', function(done) {
            var psql = new PSQL(dbopts_auth, POOL_PARAMS, { destroyOnError: scenario.destroyOnError });
            var sql = "BEGIN; select error; COMMIT;";

            psql.eventedQuery(sql, function(err, query) {

                query.on('error', function(err) {
                    assert.ok(err);
                    assert.equal(err.message, 'column "error" does not exist');
                });

                query.on('end', function() {
                    psql.eventedQuery('select 1 as foo', scenario.assertFn(done));
                });

            });
        });
    });

});
<commit_msg>Remove `end` listener, no longer emmited if error<commit_after>'use strict';

require('../setup');

var _ = require('underscore');
var assert = require('assert');
var PSQL = require('../../lib/psql');
var pg = require('pg');

var dbopts_auth = {
    host: global.settings.db_host,
    port: global.settings.db_port,
    user: _.template(global.settings.db_user, {user_id: 1}),
    dbname: _.template(global.settings.db_base_name, {user_id: 1}),
    pass: _.template(global.settings.db_user_pass, {user_id: 1})
};

var POOL_PARAMS = {};

var TRANSACTION_ABORTED_ERR_MESSAGE = 'current transaction is aborted, commands ignored until end of transaction block';

describe('transaction', function() {

    beforeEach(function() {
        pg.pools.all = {};
    });

    var querySuite = [
        {
            destroyOnError: true,
            assertFn: function validate(done) {
                return function(err, result) {
                    assert.ok(result);
                    assert.equal(result.rows[0].foo, 1);
                    done();
                };
            }
        },
        {
            destroyOnError: false,
            assertFn: function validate(done) {
                return function(err) {
                    assert.ok(err);
                    assert.equal(err.message, TRANSACTION_ABORTED_ERR_MESSAGE);
                    done();
                };
            }
        }
    ];

    querySuite.forEach(function(scenario) {
        var shouldOrShouldNot = scenario.destroyOnError ? ' ' : ' NOT ';
        it('should' + shouldOrShouldNot + 'run query after transaction fails', function(done) {
            var psql = new PSQL(dbopts_auth, POOL_PARAMS, { destroyOnError: scenario.destroyOnError });
            var sql = "BEGIN; select error; COMMIT;";
            psql.query(sql, function(err) {
                assert.ok(err);
                assert.equal(err.message, 'column "error" does not exist');
                psql.query('select 1 as foo', scenario.assertFn(done));
            });
        });
    });

    var eventedQuerySuite = [
        {
            destroyOnError: true,
            assertFn: function validate(done) {
                return function(err, query) {
                    query.on('row', function(row) {
                        assert.ok(row);
                        assert.equal(row.foo, 1);
                        done();
                    });
                };
            }
        },
        {
            destroyOnError: false,
            assertFn: function validate(done) {
                return function(err, query) {
                    query.on('error', function(err) {
                        assert.ok(err);
                        assert.equal(err.message, TRANSACTION_ABORTED_ERR_MESSAGE);
                        done();
                    });
                };
            }
        }
    ];

    eventedQuerySuite.forEach(function(scenario) {
        var shouldOrShouldNot = scenario.destroyOnError ? ' ' : ' NOT ';
        it('should' + shouldOrShouldNot + 'run evented query after transaction fails', function(done) {
            var psql = new PSQL(dbopts_auth, POOL_PARAMS, { destroyOnError: scenario.destroyOnError });
            var sql = "BEGIN; select error; COMMIT;";

            psql.eventedQuery(sql, function(err, query) {

                query.on('error', function(err) {
                    assert.ok(err);
                    assert.equal(err.message, 'column "error" does not exist');
                    psql.eventedQuery('select 1 as foo', scenario.assertFn(done));
                });
            });
        });
    });

});
<|endoftext|>


<commit_before>/*global define*/
define(['../../Core/BoundingSphere',
        '../../Core/Cartesian2',
        '../../Core/defaultValue',
        '../../Core/defined',
        '../../Core/DeveloperError',
        '../../Core/defineProperties',
        '../../Core/Event',
        '../../Core/EventHelper',
        '../../Core/ScreenSpaceEventType',
        '../../Core/wrapFunction',
        '../../Scene/SceneMode',
        '../../DynamicScene/DynamicObjectView',
        '../../ThirdParty/knockout'
    ], function(
        BoundingSphere,
        Cartesian2,
        defaultValue,
        defined,
        DeveloperError,
        defineProperties,
        Event,
        EventHelper,
        ScreenSpaceEventType,
        wrapFunction,
        SceneMode,
        DynamicObjectView,
        knockout) {
    "use strict";

    /**
     * A mixin which adds behavior to the Viewer widget for dealing with DynamicObject instances.
     * This allows for DynamicObjects to be tracked with the camera, either by the viewer clicking
     * on them, or by setting the trackedObject property.
     * Rather than being called directly, this function is normally passed as
     * a parameter to {@link Viewer#extend}, as shown in the example below.
     * @exports viewerDynamicObjectMixin
     *
     * @param {Viewer} viewer The viewer instance.
     *
     * @exception {DeveloperError} viewer is required.
     * @exception {DeveloperError} trackedObject is already defined by another mixin.
     * @exception {DeveloperError} selectedObject is already defined by another mixin.
     *
     * @example
     * // Add support for working with DynamicObject instances to the Viewer.
     * var dynamicObject = ... //A Cesium.DynamicObject instance
     * var viewer = new Cesium.Viewer('cesiumContainer');
     * viewer.extend(Cesium.viewerDynamicObjectMixin);
     * viewer.trackedObject = dynamicObject; //Camera will now track dynamicObject
     * viewer.selectedObject = object; //Selection will now appear over object
     */

    var viewerDynamicObjectMixin = function(viewer) {
        //>>includeStart('debug', pragmas.debug);
        if (!defined(viewer)) {
            throw new DeveloperError('viewer is required.');
        }
        if (viewer.hasOwnProperty('trackedObject')) {
            throw new DeveloperError('trackedObject is already defined by another mixin.');
        }
        if (viewer.hasOwnProperty('selectedObject')) {
            throw new DeveloperError('selectedObject is already defined by another mixin.');
        }
        //>>includeEnd('debug');

        var infoBox = viewer.infoBox;
        var infoBoxViewModel = defined(infoBox) ? infoBox.viewModel : undefined;

        var selectionIndicator = viewer.selectionIndicator;
        var selectionIndicatorViewModel = defined(selectionIndicator) ? selectionIndicator.viewModel : undefined;

        var enableInfoOrSelection = defined(infoBox) || defined(selectionIndicator);

        var eventHelper = new EventHelper();
        var selectedObjectObservable = knockout.observable();
        var trackedObjectObservable = knockout.observable();
        var dynamicObjectView;

        function trackSelectedObject() {
            viewer.trackedObject = viewer.selectedObject;
        }

        function infoBoxClosed() {
            viewer.selectedObject = undefined;
        }

        if (defined(infoBoxViewModel)) {
            eventHelper.add(infoBoxViewModel.cameraClicked, trackSelectedObject);
            eventHelper.add(infoBoxViewModel.closeClicked, infoBoxClosed);
        }

        var scratchVertexPositions;
        var scratchBoundingSphere;

        //Subscribe to onTick so that we can update the view each update.
        function onTick(clock) {
            var time = clock.currentTime;
            if (defined(dynamicObjectView)) {
                dynamicObjectView.update(time);
            }

            var selectedObject = viewer.selectedObject;
            var showSelection = defined(selectedObject) && enableInfoOrSelection;
            if (showSelection) {
                var oldPosition = defined(selectionIndicatorViewModel) ? selectionIndicatorViewModel.position : undefined;
                var position;
                var enableCamera = false;

                if (selectedObject.isAvailable(time)) {
                    if (defined(selectedObject.position)) {
                        position = selectedObject.position.getValue(time, oldPosition);
                        enableCamera = defined(position) && (viewer.trackedObject !== viewer.selectedObject);
                    } else if (defined(selectedObject.vertexPositions)) {
                        scratchVertexPositions = selectedObject.vertexPositions.getValue(time, scratchVertexPositions);
                        scratchBoundingSphere = BoundingSphere.fromPoints(scratchVertexPositions, scratchBoundingSphere);
                        position = scratchBoundingSphere.center;
                        // Can't track scratch positions: "enableCamera" is false.
                    }
                    // else "position" is undefined and "enableCamera" is false.
                }
                // else "position" is undefined and "enableCamera" is false.

                if (defined(selectionIndicatorViewModel)) {
                    selectionIndicatorViewModel.position = position;
                }

                if (defined(infoBoxViewModel)) {
                    infoBoxViewModel.enableCamera = enableCamera;
                    infoBoxViewModel.isCameraTracking = (viewer.trackedObject === viewer.selectedObject);

                    if (defined(selectedObject.description)) {
                        infoBoxViewModel.descriptionRawHtml = defaultValue(selectedObject.description.getValue(time), '');
                    } else {
                        infoBoxViewModel.descriptionRawHtml = '';
                    }
                }
            }

            if (defined(selectionIndicatorViewModel)) {
                selectionIndicatorViewModel.update();
                selectionIndicatorViewModel.showSelection = showSelection;
            }
            if (defined(infoBoxViewModel)) {
                infoBoxViewModel.showInfo = showSelection;
            }
        }
        eventHelper.add(viewer.clock.onTick, onTick);

        function pickAndTrackObject(e) {
            var picked = viewer.scene.pick(e.position);
            if (defined(picked) && defined(picked.primitive) && defined(picked.primitive.dynamicObject)) {
                viewer.trackedObject = picked.primitive.dynamicObject;
            }
        }

        function pickAndShowSelection(e) {
            var picked = viewer.scene.pick(e.position);
            if (defined(picked) && defined(picked.primitive) && defined(picked.primitive.dynamicObject)) {
                viewer.selectedObject = picked.primitive.dynamicObject;
            } else {
                viewer.selectedObject = undefined;
            }
        }

        function clearTrackedObject() {
            viewer.trackedObject = undefined;
        }

        function clearObjects() {
            viewer.trackedObject = undefined;
            viewer.selectedObject = undefined;
        }

        //Subscribe to the home button beforeExecute event if it exists,
        // so that we can clear the trackedObject.
        if (defined(viewer.homeButton)) {
            eventHelper.add(viewer.homeButton.viewModel.command.beforeExecute, clearTrackedObject);
        }

        //Subscribe to the geocoder search if it exists, so that we can
        //clear the trackedObject when it is clicked.
        if (defined(viewer.geocoder)) {
            eventHelper.add(viewer.geocoder.viewModel.search.beforeExecute, clearObjects);
        }

        //We need to subscribe to the data sources and collections so that we can clear the
        //tracked object when it is removed from the scene.
        function onDynamicCollectionChanged(collection, added, removed) {
            var length = removed.length;
            for (var i = 0; i < length; i++) {
                var removedObject = removed[i];
                if (viewer.trackedObject === removedObject) {
                    viewer.homeButton.viewModel.command();
                }
                if (viewer.selectedObject === removedObject) {
                    viewer.selectedObject = undefined;
                }
            }
        }

        function dataSourceAdded(dataSourceCollection, dataSource) {
            dataSource.getDynamicObjectCollection().collectionChanged.addEventListener(onDynamicCollectionChanged);
        }

        function dataSourceRemoved(dataSourceCollection, dataSource) {
            dataSource.getDynamicObjectCollection().collectionChanged.removeEventListener(onDynamicCollectionChanged);

            if (defined(viewer.trackedObject)) {
                if (dataSource.getDynamicObjectCollection().getById(viewer.trackedObject.id) === viewer.trackedObject) {
                    viewer.homeButton.viewModel.command();
                }
            }
            if (defined(viewer.selectedObject)) {
                if (dataSource.getDynamicObjectCollection().getById(viewer.selectedObject.id) === viewer.selectedObject) {
                    viewer.selectedObject = undefined;
                }
            }
        }

        //Subscribe to current data sources
        var dataSources = viewer.dataSources;
        var dataSourceLength = dataSources.getLength();
        for (var i = 0; i < dataSourceLength; i++) {
            dataSourceAdded(dataSources, dataSources.get(i));
        }

        //Hook up events so that we can subscribe to future sources.
        eventHelper.add(viewer.dataSources.dataSourceAdded, dataSourceAdded);
        eventHelper.add(viewer.dataSources.dataSourceRemoved, dataSourceRemoved);

        //Subscribe to left clicks and zoom to the picked object.
        viewer.screenSpaceEventHandler.setInputAction(pickAndShowSelection, ScreenSpaceEventType.LEFT_CLICK);
        viewer.screenSpaceEventHandler.setInputAction(pickAndTrackObject, ScreenSpaceEventType.LEFT_DOUBLE_CLICK);

        /**
         * Gets or sets the DynamicObject instance currently being tracked by the camera.
         * @memberof viewerDynamicObjectMixin.prototype
         * @type {DynamicObject}
         */
        viewer.trackedObject = undefined;
        knockout.defineProperty(viewer, 'trackedObject', {
            get : function() {
                return trackedObjectObservable();
            },
            set : function(value) {
                var sceneMode = viewer.scene.getFrameState().mode;

                if (sceneMode === SceneMode.COLUMBUS_VIEW || sceneMode === SceneMode.SCENE2D) {
                    viewer.scene.getScreenSpaceCameraController().enableTranslate = !defined(value);
                }

                if (sceneMode === SceneMode.COLUMBUS_VIEW || sceneMode === SceneMode.SCENE3D) {
                    viewer.scene.getScreenSpaceCameraController().enableTilt = !defined(value);
                }

                if (trackedObjectObservable() !== value) {
                    dynamicObjectView = (defined(value) && defined(value.position)) ?
                        new DynamicObjectView(value, viewer.scene, viewer.centralBody.getEllipsoid()) : undefined;
                    trackedObjectObservable(value);
                }
            }
        });

        /**
         * Gets or sets the object instance for which to display a selection indicator
         * @memberof viewerDynamicObjectMixin.prototype
         * @type {DynamicObject}
         */
        viewer.selectedObject = undefined;
        knockout.defineProperty(viewer, 'selectedObject', {
            get : function() {
                return selectedObjectObservable();
            },
            set : function(value) {
                if (selectedObjectObservable() !== value) {
                    if (defined(value)) {
                        if (defined(infoBoxViewModel)) {
                            infoBoxViewModel.titleText = defined(value.name) ? value.name : value.id;
                        }
                        if (defined(selectionIndicatorViewModel)) {
                            selectionIndicatorViewModel.animateAppear();
                        }
                    } else {
                        // Leave the info text in place here, it is needed during the exit animation.
                        if (defined(selectionIndicatorViewModel)) {
                            selectionIndicatorViewModel.animateDepart();
                        }
                    }
                    selectedObjectObservable(value);
                }
            }
        });

        //Wrap destroy to clean up event subscriptions.
        viewer.destroy = wrapFunction(viewer, viewer.destroy, function() {
            eventHelper.removeAll();
            viewer.screenSpaceEventHandler.removeInputAction(ScreenSpaceEventType.LEFT_CLICK);
            viewer.screenSpaceEventHandler.removeInputAction(ScreenSpaceEventType.LEFT_DOUBLE_CLICK);

            //Unsubscribe from data sources
            var dataSources = viewer.dataSources;
            var dataSourceLength = dataSources.getLength();
            for (var i = 0; i < dataSourceLength; i++) {
                dataSourceRemoved(dataSources, dataSources.get(i));
            }
        });
    };

    return viewerDynamicObjectMixin;
});
<commit_msg>Subscribe to trackedObject and selectedObject instead of creating computed observables that only exist to trigger side effects.<commit_after>/*global define*/
define(['../../Core/BoundingSphere',
        '../../Core/defaultValue',
        '../../Core/defined',
        '../../Core/DeveloperError',
        '../../Core/EventHelper',
        '../../Core/ScreenSpaceEventType',
        '../../Core/wrapFunction',
        '../../Scene/SceneMode',
        '../subscribeAndEvaluate',
        '../../DynamicScene/DynamicObjectView',
        '../../ThirdParty/knockout'
    ], function(
        BoundingSphere,
        defaultValue,
        defined,
        DeveloperError,
        EventHelper,
        ScreenSpaceEventType,
        wrapFunction,
        SceneMode,
        subscribeAndEvaluate,
        DynamicObjectView,
        knockout) {
    "use strict";

    /**
     * A mixin which adds behavior to the Viewer widget for dealing with DynamicObject instances.
     * This allows for DynamicObjects to be tracked with the camera, either by the viewer clicking
     * on them, or by setting the trackedObject property.
     * Rather than being called directly, this function is normally passed as
     * a parameter to {@link Viewer#extend}, as shown in the example below.
     * @exports viewerDynamicObjectMixin
     *
     * @param {Viewer} viewer The viewer instance.
     *
     * @exception {DeveloperError} viewer is required.
     * @exception {DeveloperError} trackedObject is already defined by another mixin.
     * @exception {DeveloperError} selectedObject is already defined by another mixin.
     *
     * @example
     * // Add support for working with DynamicObject instances to the Viewer.
     * var dynamicObject = ... //A Cesium.DynamicObject instance
     * var viewer = new Cesium.Viewer('cesiumContainer');
     * viewer.extend(Cesium.viewerDynamicObjectMixin);
     * viewer.trackedObject = dynamicObject; //Camera will now track dynamicObject
     * viewer.selectedObject = object; //Selection will now appear over object
     */

    var viewerDynamicObjectMixin = function(viewer) {
        //>>includeStart('debug', pragmas.debug);
        if (!defined(viewer)) {
            throw new DeveloperError('viewer is required.');
        }
        if (viewer.hasOwnProperty('trackedObject')) {
            throw new DeveloperError('trackedObject is already defined by another mixin.');
        }
        if (viewer.hasOwnProperty('selectedObject')) {
            throw new DeveloperError('selectedObject is already defined by another mixin.');
        }
        //>>includeEnd('debug');

        var infoBox = viewer.infoBox;
        var infoBoxViewModel = defined(infoBox) ? infoBox.viewModel : undefined;

        var selectionIndicator = viewer.selectionIndicator;
        var selectionIndicatorViewModel = defined(selectionIndicator) ? selectionIndicator.viewModel : undefined;

        var enableInfoOrSelection = defined(infoBox) || defined(selectionIndicator);

        var eventHelper = new EventHelper();
        var dynamicObjectView;

        function trackSelectedObject() {
            viewer.trackedObject = viewer.selectedObject;
        }

        function clearTrackedObject() {
            viewer.trackedObject = undefined;
        }

        function clearSelectedObject() {
            viewer.selectedObject = undefined;
        }

        function clearObjects() {
            viewer.trackedObject = undefined;
            viewer.selectedObject = undefined;
        }

        if (defined(infoBoxViewModel)) {
            eventHelper.add(infoBoxViewModel.cameraClicked, trackSelectedObject);
            eventHelper.add(infoBoxViewModel.closeClicked, clearSelectedObject);
        }

        var scratchVertexPositions;
        var scratchBoundingSphere;

        // Subscribe to onTick so that we can update the view each update.
        function onTick(clock) {
            var time = clock.currentTime;
            if (defined(dynamicObjectView)) {
                dynamicObjectView.update(time);
            }

            var selectedObject = viewer.selectedObject;
            var showSelection = defined(selectedObject) && enableInfoOrSelection;
            if (showSelection) {
                var oldPosition = defined(selectionIndicatorViewModel) ? selectionIndicatorViewModel.position : undefined;
                var position;
                var enableCamera = false;

                if (selectedObject.isAvailable(time)) {
                    if (defined(selectedObject.position)) {
                        position = selectedObject.position.getValue(time, oldPosition);
                        enableCamera = defined(position) && (viewer.trackedObject !== viewer.selectedObject);
                    } else if (defined(selectedObject.vertexPositions)) {
                        scratchVertexPositions = selectedObject.vertexPositions.getValue(time, scratchVertexPositions);
                        scratchBoundingSphere = BoundingSphere.fromPoints(scratchVertexPositions, scratchBoundingSphere);
                        position = scratchBoundingSphere.center;
                        // Can't track scratch positions: "enableCamera" is false.
                    }
                    // else "position" is undefined and "enableCamera" is false.
                }
                // else "position" is undefined and "enableCamera" is false.

                if (defined(selectionIndicatorViewModel)) {
                    selectionIndicatorViewModel.position = position;
                }

                if (defined(infoBoxViewModel)) {
                    infoBoxViewModel.enableCamera = enableCamera;
                    infoBoxViewModel.isCameraTracking = (viewer.trackedObject === viewer.selectedObject);

                    if (defined(selectedObject.description)) {
                        infoBoxViewModel.descriptionRawHtml = defaultValue(selectedObject.description.getValue(time), '');
                    } else {
                        infoBoxViewModel.descriptionRawHtml = '';
                    }
                }
            }

            if (defined(selectionIndicatorViewModel)) {
                selectionIndicatorViewModel.update();
                selectionIndicatorViewModel.showSelection = showSelection;
            }

            if (defined(infoBoxViewModel)) {
                infoBoxViewModel.showInfo = showSelection;
            }
        }
        eventHelper.add(viewer.clock.onTick, onTick);

        function pickAndTrackObject(e) {
            var picked = viewer.scene.pick(e.position);
            if (defined(picked) && defined(picked.primitive) && defined(picked.primitive.dynamicObject)) {
                viewer.trackedObject = picked.primitive.dynamicObject;
            }
        }

        function pickAndSelectObject(e) {
            var picked = viewer.scene.pick(e.position);
            if (defined(picked) && defined(picked.primitive) && defined(picked.primitive.dynamicObject)) {
                viewer.selectedObject = picked.primitive.dynamicObject;
            } else {
                viewer.selectedObject = undefined;
            }
        }

        // Subscribe to the home button beforeExecute event if it exists,
        // so that we can clear the trackedObject.
        if (defined(viewer.homeButton)) {
            eventHelper.add(viewer.homeButton.viewModel.command.beforeExecute, clearTrackedObject);
        }

        // Subscribe to the geocoder search if it exists, so that we can
        // clear the trackedObject when it is clicked.
        if (defined(viewer.geocoder)) {
            eventHelper.add(viewer.geocoder.viewModel.search.beforeExecute, clearObjects);
        }

        // We need to subscribe to the data sources and collections so that we can clear the
        // tracked object when it is removed from the scene.
        function onDynamicCollectionChanged(collection, added, removed) {
            var length = removed.length;
            for (var i = 0; i < length; i++) {
                var removedObject = removed[i];
                if (viewer.trackedObject === removedObject) {
                    viewer.homeButton.viewModel.command();
                }
                if (viewer.selectedObject === removedObject) {
                    viewer.selectedObject = undefined;
                }
            }
        }

        function dataSourceAdded(dataSourceCollection, dataSource) {
            var dynamicObjectCollection = dataSource.getDynamicObjectCollection();
            dynamicObjectCollection.collectionChanged.addEventListener(onDynamicCollectionChanged);
        }

        function dataSourceRemoved(dataSourceCollection, dataSource) {
            var dynamicObjectCollection = dataSource.getDynamicObjectCollection();
            dynamicObjectCollection.collectionChanged.removeEventListener(onDynamicCollectionChanged);

            if (defined(viewer.trackedObject)) {
                if (dynamicObjectCollection.getById(viewer.trackedObject.id) === viewer.trackedObject) {
                    viewer.homeButton.viewModel.command();
                }
            }

            if (defined(viewer.selectedObject)) {
                if (dynamicObjectCollection.getById(viewer.selectedObject.id) === viewer.selectedObject) {
                    viewer.selectedObject = undefined;
                }
            }
        }

        // Subscribe to current data sources
        var dataSources = viewer.dataSources;
        var dataSourceLength = dataSources.getLength();
        for (var i = 0; i < dataSourceLength; i++) {
            dataSourceAdded(dataSources, dataSources.get(i));
        }

        // Hook up events so that we can subscribe to future sources.
        eventHelper.add(viewer.dataSources.dataSourceAdded, dataSourceAdded);
        eventHelper.add(viewer.dataSources.dataSourceRemoved, dataSourceRemoved);

        // Subscribe to left clicks and zoom to the picked object.
        viewer.screenSpaceEventHandler.setInputAction(pickAndSelectObject, ScreenSpaceEventType.LEFT_CLICK);
        viewer.screenSpaceEventHandler.setInputAction(pickAndTrackObject, ScreenSpaceEventType.LEFT_DOUBLE_CLICK);

        /**
         * Gets or sets the DynamicObject instance currently being tracked by the camera.
         * @memberof viewerDynamicObjectMixin.prototype
         * @type {DynamicObject}
         */
        viewer.trackedObject = undefined;

        /**
         * Gets or sets the object instance for which to display a selection indicator.
         * @memberof viewerDynamicObjectMixin.prototype
         * @type {DynamicObject}
         */
        viewer.selectedObject = undefined;

        knockout.track(viewer, ['trackedObject', 'selectedObject']);

        var trackedObjectSubscription = subscribeAndEvaluate(viewer, 'trackedObject', function(value) {
            var scene = viewer.scene;
            var sceneMode = scene.getFrameState().mode;
            var isTracking = defined(value);

            if (sceneMode === SceneMode.COLUMBUS_VIEW || sceneMode === SceneMode.SCENE2D) {
                scene.getScreenSpaceCameraController().enableTranslate = !isTracking;
            }

            if (sceneMode === SceneMode.COLUMBUS_VIEW || sceneMode === SceneMode.SCENE3D) {
                scene.getScreenSpaceCameraController().enableTilt = !isTracking;
            }

            if (isTracking && defined(value.position)) {
                dynamicObjectView = new DynamicObjectView(value, scene, viewer.centralBody.getEllipsoid());
            } else {
                dynamicObjectView = undefined;
            }
        });

        var selectedObjectSubscription = subscribeAndEvaluate(viewer, 'selectedObject', function(value) {
            if (defined(value)) {
                if (defined(infoBoxViewModel)) {
                    infoBoxViewModel.titleText = defined(value.name) ? value.name : value.id;
                }
                if (defined(selectionIndicatorViewModel)) {
                    selectionIndicatorViewModel.animateAppear();
                }
            } else {
                // Leave the info text in place here, it is needed during the exit animation.
                if (defined(selectionIndicatorViewModel)) {
                    selectionIndicatorViewModel.animateDepart();
                }
            }
        });

        // Wrap destroy to clean up event subscriptions.
        viewer.destroy = wrapFunction(viewer, viewer.destroy, function() {
            eventHelper.removeAll();
            trackedObjectSubscription.dispose();
            selectedObjectSubscription.dispose();
            viewer.screenSpaceEventHandler.removeInputAction(ScreenSpaceEventType.LEFT_CLICK);
            viewer.screenSpaceEventHandler.removeInputAction(ScreenSpaceEventType.LEFT_DOUBLE_CLICK);

            // Unsubscribe from data sources
            var dataSources = viewer.dataSources;
            var dataSourceLength = dataSources.getLength();
            for (var i = 0; i < dataSourceLength; i++) {
                dataSourceRemoved(dataSources, dataSources.get(i));
            }
        });
    };

    return viewerDynamicObjectMixin;
});
<|endoftext|>


<commit_before>// ==========================================================================
// Project:   MySystem.Node Fixtures
// Copyright: ©2010 My Company, Inc.
// ==========================================================================
/*globals MySystem */

sc_require('models/node');

MySystem.Node.FIXTURES = [

  { guid: '1',
    image: '',
    title: 'First Node',
    inputs: ['2'],
    outputs: ['2']
  },

  { guid: '2',
    image: '',
    title: 'Second Node',
    inputs: ['1'],
    output: ['1']
  }

];
<commit_msg>Remove links from fixtures.<commit_after>// ==========================================================================
// Project:   MySystem.Node Fixtures
// Copyright: ©2010 My Company, Inc.
// ==========================================================================
/*globals MySystem */

sc_require('models/node');

MySystem.Node.FIXTURES = [

  { guid: '1',
    image: '',
    title: 'First Node',
    inputs: [],
    outputs: []
  },

  { guid: '2',
    image: '',
    title: 'Second Node',
    inputs: [],
    output: []
  }

];
<|endoftext|>


<commit_before>// @require: OpenLayers/Tile.js
/**
* @class
*/
OpenLayers.Tile.WFS = Class.create();
OpenLayers.Tile.WFS.prototype =
  Object.extend( new OpenLayers.Tile(), {

    /** @type Array of Function */
    handlers: null,

    /** @type Array of */
    features: null,

    /**
    * @constructor
    *
    * @param {OpenLayers.Grid} grid
    * @param {OpenLayers.Bounds} bounds
    * @param {String} url
    * @param {OpenLayers.Size} size
    */
    initialize: function(grid, bounds, url, size) {
        OpenLayers.Tile.prototype.initialize.apply(this, arguments);

        this.features = new Array();

        this.handlers = new Array();
        this.handlers["requestSuccess"] = this.requestSuccess;
    },

    /**
    */
    draw:function() {
        OpenLayers.Tile.prototype.draw.apply(this, arguments);
        this.loadFeaturesForRegion("requestSuccess");
    },

    /**
     * @param OpenLayers.Pixel
     */
    setPosition:function(pixel) {
        this.position = pixel;
    },

    /** get the full request string from the ds and the tile params
    *     and call the AJAX loadURL().
    *
    *     input are function pointers for what to do on success and failure.
    *
    * @param {function} success
    * @param {function} failure
    */
    loadFeaturesForRegion:function(success, failure) {

        if (!this.loaded) {

            if (this.url != "") {

                // TODO: Hmmm, this stops multiple loads of the data when a
                //       result isn't immediately retrieved, but it's hacky.
                //       Do it better.
                this.loaded = true;
//                ol.Log.info("request string: " + this.url);
                OpenLayers.loadURL(this.url, null, this, success, failure);
            }
        }
    },

    /** Return from AJAX request
    *
    * @param {} request
    */
    requestSuccess:function(request) {
        var doc = request.responseXML;

        if (!doc || request.fileType!="XML") {
            doc = OpenLayers.parseXMLString(request.responseText);
        }

        var resultFeatures = OpenLayers.Util.getNodes(doc, "gml:featureMember");
//        ol.Log.info(this.grid.name + " found " +
//                     resultFeatures.length + " features");

        //clear old featureList
        this.features = new Array();

        for (var i=0; i < resultFeatures.length; i++) {

            //make new Feature
            var feature = new OpenLayers.Feature(resultFeatures[i]);
            this.features.append(feature);

            //make new Icon
            var icon = new OpenLayers.Icon(feature.markerImage, feature.size);

            //make new marker
            var marker = new OpenLayers.Marker(feature.lonlat, icon);
            this.addMarker(marker);
    	}

    },

    /** @final @type String */
    CLASS_NAME: "OpenLayers.Tile.WFS"
  }
);

<commit_msg>WFS tiles will keep track of markers, not features. Update the requestSuccess() loop to handle new Feature architecture<commit_after>// @require: OpenLayers/Tile.js
/**
* @class
*/
OpenLayers.Tile.WFS = Class.create();
OpenLayers.Tile.WFS.prototype =
  Object.extend( new OpenLayers.Tile(), {

    /** @type Array of Function */
    handlers: null,

    /** @type Array of */
    markers: null,

    /**
    * @constructor
    *
    * @param {OpenLayers.Grid} grid
    * @param {OpenLayers.Bounds} bounds
    * @param {String} url
    * @param {OpenLayers.Size} size
    */
    initialize: function(grid, bounds, url, size) {
        OpenLayers.Tile.prototype.initialize.apply(this, arguments);

        this.markers = new Array();

        this.handlers = new Array();
        this.handlers["requestSuccess"] = this.requestSuccess;
    },

    /**
    */
    draw:function() {
        OpenLayers.Tile.prototype.draw.apply(this, arguments);
        this.loadFeaturesForRegion("requestSuccess");
    },

    /**
     * @param OpenLayers.Pixel
     */
    setPosition:function(pixel) {
        this.position = pixel;
    },

    /** get the full request string from the ds and the tile params
    *     and call the AJAX loadURL().
    *
    *     input are function pointers for what to do on success and failure.
    *
    * @param {function} success
    * @param {function} failure
    */
    loadFeaturesForRegion:function(success, failure) {

        if (!this.loaded) {

            if (this.url != "") {

                // TODO: Hmmm, this stops multiple loads of the data when a
                //       result isn't immediately retrieved, but it's hacky.
                //       Do it better.
                this.loaded = true;
//                ol.Log.info("request string: " + this.url);
                OpenLayers.loadURL(this.url, null, this, success, failure);
            }
        }
    },

    /** Return from AJAX request
    *
    * @param {} request
    */
    requestSuccess:function(request) {
        var doc = request.responseXML;

        if (!doc || request.fileType!="XML") {
            doc = OpenLayers.parseXMLString(request.responseText);
        }

        var resultFeatures = OpenLayers.Util.getNodes(doc, "gml:featureMember");
//        ol.Log.info(this.layer.name + " found " +
//                     resultFeatures.length + " features");

        //clear old featureList
        this.features = new Array();

        for (var i=0; i < resultFeatures.length; i++) {

            var feature = new MCFeature(resultFeatures[i]);
            var icon = new OpenLayers.Icon(feature.markerImage, feature.size);
            var marker = new OpenLayers.Marker(feature.lonlat, icon, feature);

            //add to local collection
            this.markers.append(marker);

            //add to layer
            this.layer.addMarker(marker);
    	}

    },

    /** @final @type String */
    CLASS_NAME: "OpenLayers.Tile.WFS"
  }
);

<|endoftext|>