<commit_before>/*
 *  alt.musica repertoire, Autumn 2018
 */

var autumnRepertoire = {

	title: 'alt.musica Repertoire for Autumn, 2018',

	description: 'Songs with winds, string quartet and rhythm section',

	selector: '#manifest-autumn-2018',

	baseUrl: 'http://davidelainealt.com/scores/',

	programOrder: [ '01', '02', '04', '03', '04' ],

	program: {
		'01': "The Optimist",
		'02': "Rising",
		'03': "Listening",
		'04': "Dreams",
		'05': "Wee"
	},

	bookOrder: [ '01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11' ],
	books: {
		'01': "Flute, Alto Flute & Piccolo",
		'02': "English Horn",
		'03': "Bass Clarinet",
		'04': "Bassoon",
		'05': "Voice",
		'06': "Drums",
		'07': "Violin I",
		'08': "Violin II",
		'09': "Viola",
		'10': "'Cello",
		'11': "Bass",
	},

	partsInBooks: {
		"Flute, Alto Flute & Piccolo": {
            "The Optimist": [ "Piccolo", "Flute" ],
            "Rising": [ "Flute" ],
            "Listening": [ "Flute" ],
            "Dreams": [ "Flute" ],
            "Wee": [ "Flute" ]
        },
		"English Horn": {
            "The Optimist": [ "English Horn in F for Clarinet" ]
            "Rising": [ "English Horn in F for Clarinet" ],
            "Listening": [ "English Horn in F for Clarinet" ],
            "Dreams": [ "English Horn in F for Clarinet" ],
            "Wee": [ "English Horn in F for Clarinet" ]
        },
		"Bass Clarinet": {
            "The Optimist": [ "Clarinet in A", "Bass Clarinet in Bb for Bassoon"  ]
            "Rising": [ "Clarinet in A" ],
            "Listening": [ "Clarinet in A" ],
            "Dreams": [ "Clarinet in A" ],
            "Wee": [ "Clarinet in A", "Bass Clarinet in Bb for Bassoon" ]
		},
		"Bassoon": {
            "The Optimist": [ "Bassoon"  ]
            "Rising": [ "Bassoon" ],
            "Listening": [ "Bassoon" ],
            "Wee": [ "Bassoon" ]
		},
		"Voice/Percussion": {
            "The Optimist": [ "Voice"  ]
            "Rising": [ "Voice" ],
            "Listening": [ "Voice" ],
		},
		"Drums": {
            "The Optimist": [ "Voice"  ]
            "Rising": [ "Bass" ],
            "Listening": [ "Voice" ],
            "Dreams": [ "Violin II" ],
            "Wee": [ "Flute" ]
		},
		"Violin I": {
            "The Optimist": [ "Violin I"  ]
            "Rising": [ "Violin I" ],
            "Listening": [ "Violin I" ],
            "Dreams": [ "Violin I" ],
            "Wee": [ "Violin I" ]
		},
		"Violin II": {
            "The Optimist": [ "Violin II"  ]
            "Rising": [ "Violin II" ],
            "Listening": [ "Violin II" ],
            "Dreams": [ "Violin II" ],
            "Wee": [ "Violin II" ]
		},
		"Viola": {
            "The Optimist": [ "Viola"  ]
            "Rising": [ "Viola" ],
            "Listening": [ "Viola" ],
            "Dreams": [ "Viola" ],
            "Wee": [ "Viola" ]
		},
		"'Cello": {
            "The Optimist": [ "'Cello"  ]
            "Rising": [ "'Cello" ],
            "Listening": [ "'Cello" ],
            "Dreams": [ "'Cello" ],
            "Wee": [ "'Cello" ]
		},
		"Bass": {
            "The Optimist": [ "Bass"  ]
            "Rising": [ "Bass" ],
            "Listening": [ "Bass" ],
            "Dreams": [ "Bass" ],
            "Wee": [ "Bass" ]
		}
	},

	songs: {

		"The Optimist": {
			metadata: {
				title: "The Optimist",
				composer: "Aeion Solar",
				arranger: "D. Elaine Alt",
				genre: "Classical",
				bpm: '116',
				key: "B"
			},
			fileLocation: "alt.musica",
			filePrefix: "optimist/strings/optimist-",
			parts: {
				"Piccolo": { fileSuffix: "Piccolo" },
				"Flute": { fileSuffix: "Flute" },
				"English Horn in F for Clarinet": { fileSuffix: "English-Horn-in-F" },
				"Clarinet in A": { fileSuffix: "Clarinet-in-A" },
				"Bass Clarinet in Bb for Bassoon": { fileSuffix: "Bass-Clarinet-in-Bb" },
				"Bassoon": { fileSuffix: "Bassoon" },
				"Voice": { fileSuffix: "Voice" },
				"Violin I": { fileSuffix: "Violin-I" },
				"Violin II": { fileSuffix: "Violin-II" },
				"Viola": { fileSuffix: "Viola" },
				"'Cello": { fileSuffix: "Cello" },
				"Bass": { fileSuffix: "Bass" }
			},
			scores: {
				"Transposed": { fileSuffix: "Score-Transposed" }
			},
			recordings: {
				original: {
					description: 'Mutual Admiration Org (no strings)',
					url: 'https://soundcloud.com/david-elaine-alt/the-optimist/s-Xmc0m'
				},
				synthesized: {
					description: 'MIDI',
					url: 'https://soundcloud.com/david-elaine-alt/the-optimist-midi/s-M74Tf'
				}
			}
		},

		"Rising": {
			metadata: {
				title: "Rising",
				composer: "Elaine Paul",
				genre: "Jazz",
				bpm: '72, 84, 118',
				key: "Ab"
			},
			fileLocation: "alt.musica",
			filePrefix: "rising/strings/rising-",
			parts: {
				"Flute": { fileSuffix: "Flute" },
				//"English Horn in F for Clarinet": { fileSuffix: "English-Horn-in-F" },
				"Clarinet in A": { fileSuffix: "Clarinet-in-A" },
				//"Bass Clarinet in Bb for Bassoon": { fileSuffix: "Bass-Clarinet-in-Bb" },
				"Bassoon": { fileSuffix: "Bassoon" },
				"Voice": { fileSuffix: "Voice" },
				"Violin I": { fileSuffix: "Violin-I" },
				"Violin II": { fileSuffix: "Violin-II" },
				"Viola": { fileSuffix: "Viola" },
				"'Cello": { fileSuffix: "Cello" },
				"Bass": { fileSuffix: "Bass" }
			},
			scores: {
				"Transposed": { fileSuffix: "Score-Transposed" }
			},
			recordings: {
				original: {
					description: 'Jazz Combustion Uprising',
					url: 'https://soundcloud.com/jazzcombustion/rising'
				},
				synthesized: {
					description: 'MIDI',
					url: 'https://soundcloud.com/david-elaine-alt/rising/s-mQjyo'
				}
			}
		},

		"Wee": {
			metadata: {
				title: "Wee",
				composer: "Sy Johnson",
				arranger: "Elaine Paul",
				genre: "Jazz",
				bpm: '108-168',
				key: "G"
			},
			fileLocation: "alt.musica",
			filePrefix: "wee/strings/wee-",
			parts: {
				"Flute": { fileSuffix: "Flute" },
				"English Horn in F for Clarinet": { fileSuffix: "English-Horn-in-F" },
				"Clarinet in A": { fileSuffix: "Clarinet-in-A" },
				"Bass Clarinet in Bb for Bassoon": { fileSuffix: "Bass-Clarinet-in-Bb" },
				"Bassoon": { fileSuffix: "Bassoon" },
				"Violin I": { fileSuffix: "Violin-I" },
				"Violin II": { fileSuffix: "Violin-II" },
				"Viola": { fileSuffix: "Viola" },
				"'Cello": { fileSuffix: "Cello" },
				"Bass": { fileSuffix: "Bass" }
			},
			scores: {
				"Transposed": { fileSuffix: "Score-Transposed" }
			},
			recordings: {
				original: {
					description: 'Charles Mingus - Mingus Moves',
					url: 'https://www.youtube.com/watch?v=g43EDq1nPHo'
				},
				woodwindsOne: {
					description: 'Mutual Admiration Org (no strings) live #1',
					url: 'https://ali-cia.net/mutualadmiration/media/mp3/mao/socha01/wee.mp3'
				},
				woodwindsTwo: {
					description: 'Mutual Admiration Org (no strings) live #2',
					url: 'https://ali-cia.net/mutualadmiration/media/mp3/mao/socha02/wee.mp3'
				},
				woodwindsThree: {
					description: 'Mutual Admiration Org (no strings) live #3',
					url: 'https://ali-cia.net/mutualadmiration/media/mp3/mao/cynthia/wee.mp3'
				}
			}
		},

		"Listening": {
			metadata: {
				title: "Listening",
				composer: "Elaine Paul",
				genre: "Ballad",
				bpm: '53',
				key: "Ab minor"
			},
			fileLocation: "alt.musica",
			filePrefix: "listening/strings/listeing-",
			parts: {
				"Flute": { fileSuffix: "Flute" },
				"Clarinet in A": { fileSuffix: "Clarinet-in-A" },
				"Bassoon": { fileSuffix: "Bassoon" },
				"Voice": { fileSuffix: "Voice" },
				"Violin I": { fileSuffix: "Violin-I" },
				"Violin II": { fileSuffix: "Violin-II" },
				"Viola": { fileSuffix: "Viola" },
				"'Cello": { fileSuffix: "Cello" },
				"Bass": { fileSuffix: "Bass" }
			},
			scores: {
				"Transposed": { fileSuffix: "Score-Transposed" }
			},
			recordings: {
				woodwinds: {
					description: 'Mutual Admiration Org (no strings)',
					url: 'https://soundcloud.com/alicia-voice/listening'
				},
				synthesized: {
					description: 'MIDI',
					url: 'https://soundcloud.com/david-elaine-alt/listening-midi/s-tjbNk'
				}
			}
		},

		"Dreams": {
			metadata: {
				title: "Dreams",
				composer: "Elaine Paul",
				genre: "Classical",
				bpm: '144',
				key: "D"
			},
			fileLocation: "alt.musica",
			filePrefix: "dreams/strings/dreams-",
			parts: {
				"Flute": { fileSuffix: "Flute" },
				"Clarinet in A": { fileSuffix: "Clarinet-in-A" },
				"Violin I": { fileSuffix: "Violin-I" },
				"Violin II": { fileSuffix: "Violin-II" },
				"Viola": { fileSuffix: "Viola" },
				"'Cello": { fileSuffix: "Cello" }
			},
			scores: {
				"Transposed": { fileSuffix: "Score-Transposed" }
			},
			recordings: {
				original: {
					description: 'ACMAP Sextet',
					url: 'https://soundcloud.com/david-elaine-alt/dreams'
				}
			}
		}

	}
};

var manifest = new Manifest(autumnRepertoire);
var content = manifest.displayManifest() ;
manifest.placeManifestOnReady(content) ;

<commit_msg>bugfix Autumn 2018<commit_after>/*
 *  alt.musica repertoire, Autumn 2018
 */

var autumnRepertoire = {

	title: 'alt.musica Repertoire for Autumn, 2018',

	description: 'Songs with winds, string quartet and rhythm section',

	selector: '#manifest-autumn-2018',

	baseUrl: 'http://davidelainealt.com/scores/',

	programOrder: [ '01', '02', '04', '03', '04' ],

	program: {
		'01': "The Optimist",
		'02': "Rising",
		'03': "Listening",
		'04': "Dreams",
		'05': "Wee"
	},

	bookOrder: [ '01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11' ],
	books: {
		'01': "Flute, Alto Flute & Piccolo",
		'02': "English Horn",
		'03': "Bass Clarinet",
		'04': "Bassoon",
		'05': "Voice",
		'06': "Drums",
		'07': "Violin I",
		'08': "Violin II",
		'09': "Viola",
		'10': "'Cello",
		'11': "Bass",
	},

	partsInBooks: {
		"Flute, Alto Flute & Piccolo": {
            "The Optimist": [ "Piccolo", "Flute" ],
            "Rising": [ "Flute" ],
            "Listening": [ "Flute" ],
            "Dreams": [ "Flute" ],
            "Wee": [ "Flute" ]
        },
		"English Horn": {
            "The Optimist": [ "English Horn in F for Clarinet" ],
            "Rising": [ "English Horn in F for Clarinet" ],
            "Listening": [ "English Horn in F for Clarinet" ],
            "Dreams": [ "English Horn in F for Clarinet" ],
            "Wee": [ "English Horn in F for Clarinet" ]
        },
		"Bass Clarinet": {
            "The Optimist": [ "Clarinet in A", "Bass Clarinet in Bb for Bassoon" ],
            "Rising": [ "Clarinet in A" ],
            "Listening": [ "Clarinet in A" ],
            "Dreams": [ "Clarinet in A" ],
            "Wee": [ "Clarinet in A", "Bass Clarinet in Bb for Bassoon" ]
		},
		"Bassoon": {
            "The Optimist": [ "Bassoon" ],
            "Rising": [ "Bassoon" ],
            "Listening": [ "Bassoon" ],
            "Wee": [ "Bassoon" ]
		},
		"Voice/Percussion": {
            "The Optimist": [ "Voice" ],
            "Rising": [ "Voice" ],
            "Listening": [ "Voice" ],
		},
		"Drums": {
            "The Optimist": [ "Voice" ],
            "Rising": [ "Bass" ],
            "Listening": [ "Voice" ],
            "Dreams": [ "Violin II" ],
            "Wee": [ "Flute" ]
		},
		"Violin I": {
            "The Optimist": [ "Violin I" ],
            "Rising": [ "Violin I" ],
            "Listening": [ "Violin I" ],
            "Dreams": [ "Violin I" ],
            "Wee": [ "Violin I" ]
		},
		"Violin II": {
            "The Optimist": [ "Violin II" ]
            "Rising": [ "Violin II" ],
            "Listening": [ "Violin II" ],
            "Dreams": [ "Violin II" ],
            "Wee": [ "Violin II" ]
		},
		"Viola": {
            "The Optimist": [ "Viola" ],
            "Rising": [ "Viola" ],
            "Listening": [ "Viola" ],
            "Dreams": [ "Viola" ],
            "Wee": [ "Viola" ]
		},
		"'Cello": {
            "The Optimist": [ "'Cello" ],
            "Rising": [ "'Cello" ],
            "Listening": [ "'Cello" ],
            "Dreams": [ "'Cello" ],
            "Wee": [ "'Cello" ]
		},
		"Bass": {
            "The Optimist": [ "Bass" ],
            "Rising": [ "Bass" ],
            "Listening": [ "Bass" ],
            "Dreams": [ "Bass" ],
            "Wee": [ "Bass" ]
		}
	},

	songs: {

		"The Optimist": {
			metadata: {
				title: "The Optimist",
				composer: "Aeion Solar",
				arranger: "D. Elaine Alt",
				genre: "Classical",
				bpm: '116',
				key: "B"
			},
			fileLocation: "alt.musica",
			filePrefix: "optimist/strings/optimist-",
			parts: {
				"Piccolo": { fileSuffix: "Piccolo" },
				"Flute": { fileSuffix: "Flute" },
				"English Horn in F for Clarinet": { fileSuffix: "English-Horn-in-F" },
				"Clarinet in A": { fileSuffix: "Clarinet-in-A" },
				"Bass Clarinet in Bb for Bassoon": { fileSuffix: "Bass-Clarinet-in-Bb" },
				"Bassoon": { fileSuffix: "Bassoon" },
				"Voice": { fileSuffix: "Voice" },
				"Violin I": { fileSuffix: "Violin-I" },
				"Violin II": { fileSuffix: "Violin-II" },
				"Viola": { fileSuffix: "Viola" },
				"'Cello": { fileSuffix: "Cello" },
				"Bass": { fileSuffix: "Bass" }
			},
			scores: {
				"Transposed": { fileSuffix: "Score-Transposed" }
			},
			recordings: {
				original: {
					description: 'Mutual Admiration Org (no strings)',
					url: 'https://soundcloud.com/david-elaine-alt/the-optimist/s-Xmc0m'
				},
				synthesized: {
					description: 'MIDI',
					url: 'https://soundcloud.com/david-elaine-alt/the-optimist-midi/s-M74Tf'
				}
			}
		},

		"Rising": {
			metadata: {
				title: "Rising",
				composer: "Elaine Paul",
				genre: "Jazz",
				bpm: '72, 84, 118',
				key: "Ab"
			},
			fileLocation: "alt.musica",
			filePrefix: "rising/strings/rising-",
			parts: {
				"Flute": { fileSuffix: "Flute" },
				//"English Horn in F for Clarinet": { fileSuffix: "English-Horn-in-F" },
				"Clarinet in A": { fileSuffix: "Clarinet-in-A" },
				//"Bass Clarinet in Bb for Bassoon": { fileSuffix: "Bass-Clarinet-in-Bb" },
				"Bassoon": { fileSuffix: "Bassoon" },
				"Voice": { fileSuffix: "Voice" },
				"Violin I": { fileSuffix: "Violin-I" },
				"Violin II": { fileSuffix: "Violin-II" },
				"Viola": { fileSuffix: "Viola" },
				"'Cello": { fileSuffix: "Cello" },
				"Bass": { fileSuffix: "Bass" }
			},
			scores: {
				"Transposed": { fileSuffix: "Score-Transposed" }
			},
			recordings: {
				original: {
					description: 'Jazz Combustion Uprising',
					url: 'https://soundcloud.com/jazzcombustion/rising'
				},
				synthesized: {
					description: 'MIDI',
					url: 'https://soundcloud.com/david-elaine-alt/rising/s-mQjyo'
				}
			}
		},

		"Wee": {
			metadata: {
				title: "Wee",
				composer: "Sy Johnson",
				arranger: "Elaine Paul",
				genre: "Jazz",
				bpm: '108-168',
				key: "G"
			},
			fileLocation: "alt.musica",
			filePrefix: "wee/strings/wee-",
			parts: {
				"Flute": { fileSuffix: "Flute" },
				"English Horn in F for Clarinet": { fileSuffix: "English-Horn-in-F" },
				"Clarinet in A": { fileSuffix: "Clarinet-in-A" },
				"Bass Clarinet in Bb for Bassoon": { fileSuffix: "Bass-Clarinet-in-Bb" },
				"Bassoon": { fileSuffix: "Bassoon" },
				"Violin I": { fileSuffix: "Violin-I" },
				"Violin II": { fileSuffix: "Violin-II" },
				"Viola": { fileSuffix: "Viola" },
				"'Cello": { fileSuffix: "Cello" },
				"Bass": { fileSuffix: "Bass" }
			},
			scores: {
				"Transposed": { fileSuffix: "Score-Transposed" }
			},
			recordings: {
				original: {
					description: 'Charles Mingus - Mingus Moves',
					url: 'https://www.youtube.com/watch?v=g43EDq1nPHo'
				},
				woodwindsOne: {
					description: 'Mutual Admiration Org (no strings) live #1',
					url: 'https://ali-cia.net/mutualadmiration/media/mp3/mao/socha01/wee.mp3'
				},
				woodwindsTwo: {
					description: 'Mutual Admiration Org (no strings) live #2',
					url: 'https://ali-cia.net/mutualadmiration/media/mp3/mao/socha02/wee.mp3'
				},
				woodwindsThree: {
					description: 'Mutual Admiration Org (no strings) live #3',
					url: 'https://ali-cia.net/mutualadmiration/media/mp3/mao/cynthia/wee.mp3'
				}
			}
		},

		"Listening": {
			metadata: {
				title: "Listening",
				composer: "Elaine Paul",
				genre: "Ballad",
				bpm: '53',
				key: "Ab minor"
			},
			fileLocation: "alt.musica",
			filePrefix: "listening/strings/listeing-",
			parts: {
				"Flute": { fileSuffix: "Flute" },
				"Clarinet in A": { fileSuffix: "Clarinet-in-A" },
				"Bassoon": { fileSuffix: "Bassoon" },
				"Voice": { fileSuffix: "Voice" },
				"Violin I": { fileSuffix: "Violin-I" },
				"Violin II": { fileSuffix: "Violin-II" },
				"Viola": { fileSuffix: "Viola" },
				"'Cello": { fileSuffix: "Cello" },
				"Bass": { fileSuffix: "Bass" }
			},
			scores: {
				"Transposed": { fileSuffix: "Score-Transposed" }
			},
			recordings: {
				woodwinds: {
					description: 'Mutual Admiration Org (no strings)',
					url: 'https://soundcloud.com/alicia-voice/listening'
				},
				synthesized: {
					description: 'MIDI',
					url: 'https://soundcloud.com/david-elaine-alt/listening-midi/s-tjbNk'
				}
			}
		},

		"Dreams": {
			metadata: {
				title: "Dreams",
				composer: "Elaine Paul",
				genre: "Classical",
				bpm: '144',
				key: "D"
			},
			fileLocation: "alt.musica",
			filePrefix: "dreams/strings/dreams-",
			parts: {
				"Flute": { fileSuffix: "Flute" },
				"Clarinet in A": { fileSuffix: "Clarinet-in-A" },
				"Violin I": { fileSuffix: "Violin-I" },
				"Violin II": { fileSuffix: "Violin-II" },
				"Viola": { fileSuffix: "Viola" },
				"'Cello": { fileSuffix: "Cello" }
			},
			scores: {
				"Transposed": { fileSuffix: "Score-Transposed" }
			},
			recordings: {
				original: {
					description: 'ACMAP Sextet',
					url: 'https://soundcloud.com/david-elaine-alt/dreams'
				}
			}
		}

	}
};

var manifest = new Manifest(autumnRepertoire);
var content = manifest.displayManifest() ;
manifest.placeManifestOnReady(content) ;

<|endoftext|>


<commit_before>'use strict';

var path = require('path');

var webpack = require('webpack');
var StatsWriterPlugin = require('webpack-stats-plugin').StatsWriterPlugin;
var ServiceWorkerPlugin = require('../plugins/service-worker');
var MiniCSSExtractPlugin = require('mini-css-extract-plugin');
var UglifyJSPlugin = require('uglifyjs-webpack-plugin');
var OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin');

var hopsConfig = require('hops-config');

var getAssetPath = path.join.bind(path, hopsConfig.assetPath);

module.exports = {
  mode: process.env.NODE_ENV === 'production' ? 'production' : 'development',
  bail: true,
  entry: require.resolve('../shims/build'),
  output: {
    path: hopsConfig.buildDir,
    publicPath: '/',
    pathinfo: true,
    filename: getAssetPath('[name]-[chunkhash:16].js'),
    chunkFilename: getAssetPath('[name]-[chunkhash:16].js'),
    devtoolModuleFilenameTemplate: function(info) {
      return path
        .relative(hopsConfig.appDir, info.absoluteResourcePath)
        .replace(/\\/g, '/');
    },
  },
  context: hopsConfig.appDir,
  resolve: require('../sections/resolve')('build'),
  module: {
    rules: require('../sections/module-rules')('build'),
  },
  devtool: 'source-map',
  optimization: {
    splitChunks: {
      chunks: 'all',
    },
    minimizer: [
      new UglifyJSPlugin({
        cache: true,
        parallel: true,
        sourceMap: true,
        uglifyOptions: {
          output: { comments: false },
        },
      }),
      new OptimizeCSSPlugin({
        cssProcessorOptions: {
          reduceIdents: { disable: true },
          zindex: { disable: true },
          mergeIdents: { disable: true },
          discardUnused: { disable: true },
        },
      }),
    ],
  },
  plugins: [
    new StatsWriterPlugin({ fields: null }),
    new ServiceWorkerPlugin(),
    new MiniCSSExtractPlugin({
      filename: getAssetPath('[name]-[contenthash:16].css'),
    }),
    new webpack.HashedModuleIdsPlugin(),
    new webpack.EnvironmentPlugin(
      Object.assign(
        {
          NODE_ENV: 'production',
        },
        hopsConfig.envVars
      )
    ),
    new webpack.optimize.ModuleConcatenationPlugin(),
  ],
  performance: {
    assetFilter: function(assetFilename) {
      return assetFilename !== 'stats.json' && !/\.map$/.test(assetFilename);
    },
  },
};
<commit_msg>fix(build): fix prefixes in dev and prod being different<commit_after>'use strict';

var path = require('path');

var webpack = require('webpack');
var StatsWriterPlugin = require('webpack-stats-plugin').StatsWriterPlugin;
var ServiceWorkerPlugin = require('../plugins/service-worker');
var MiniCSSExtractPlugin = require('mini-css-extract-plugin');
var UglifyJSPlugin = require('uglifyjs-webpack-plugin');
var OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin');

var hopsConfig = require('hops-config');

var getAssetPath = path.join.bind(path, hopsConfig.assetPath);

module.exports = {
  mode: process.env.NODE_ENV === 'production' ? 'production' : 'development',
  bail: true,
  entry: require.resolve('../shims/build'),
  output: {
    path: hopsConfig.buildDir,
    publicPath: '/',
    pathinfo: true,
    filename: getAssetPath('[name]-[chunkhash:16].js'),
    chunkFilename: getAssetPath('[name]-[chunkhash:16].js'),
    devtoolModuleFilenameTemplate: function(info) {
      return path
        .relative(hopsConfig.appDir, info.absoluteResourcePath)
        .replace(/\\/g, '/');
    },
  },
  context: hopsConfig.appDir,
  resolve: require('../sections/resolve')('build'),
  module: {
    rules: require('../sections/module-rules')('build'),
  },
  devtool: 'source-map',
  optimization: {
    splitChunks: {
      chunks: 'all',
    },
    minimizer: [
      new UglifyJSPlugin({
        cache: true,
        parallel: true,
        sourceMap: true,
        uglifyOptions: {
          output: { comments: false },
        },
      }),
      new OptimizeCSSPlugin({
        cssProcessorOptions: {
          reduceIdents: { disable: true },
          zindex: { disable: true },
          mergeIdents: { disable: true },
          discardUnused: { disable: true },
          autoprefixer: { disable: true },
        },
      }),
    ],
  },
  plugins: [
    new StatsWriterPlugin({ fields: null }),
    new ServiceWorkerPlugin(),
    new MiniCSSExtractPlugin({
      filename: getAssetPath('[name]-[contenthash:16].css'),
    }),
    new webpack.HashedModuleIdsPlugin(),
    new webpack.EnvironmentPlugin(
      Object.assign(
        {
          NODE_ENV: 'production',
        },
        hopsConfig.envVars
      )
    ),
    new webpack.optimize.ModuleConcatenationPlugin(),
  ],
  performance: {
    assetFilter: function(assetFilename) {
      return assetFilename !== 'stats.json' && !/\.map$/.test(assetFilename);
    },
  },
};
<|endoftext|>


<commit_before>
/*
Abstract Base helper class
*/
BaseHelper = function()
{
  THREE.Object3D.call( this );
}

BaseHelper.prototype = Object.create( THREE.Object3D.prototype );
BaseHelper.prototype.constructor = BaseHelper;

<commit_msg>-added show/hide methods to baseHelper<commit_after>
/*
Abstract Base helper class
*/
BaseHelper = function()
{
  THREE.Object3D.call( this );
}

BaseHelper.prototype = Object.create( THREE.Object3D.prototype );
BaseHelper.prototype.constructor = BaseHelper;

BaseHelper.prototype.hide = function () {
	this.traverse(function( child ) {
		child.visible = false;
	});
};

BaseHelper.prototype.show = function () {
	this.traverse(function( child ) {
		child.visible = true;
	});
};

<|endoftext|>


<commit_before>javascript:void(location.href='http://localhost:9999/shorten/?url='+encodeURIComponent(location.href))
<commit_msg>enable home redirect by default in the bookmarklet example code<commit_after>javascript:void(location.href='http://localhost:9999/shorten/?url='+encodeURIComponent(location.href)&home=1)
<|endoftext|>


<commit_before>// Create a circle shaped path with its center at the center
// of the view and a radius of 30:
var path = new Path.Circle({
  center: view.center,
  radius: 10,
  strokeColor: 'black',
  fillColor: 'blue'
  });

var symbol = new Symbol(path);

var placedSymbols = {};

function onResize(event) {
}

function ensureObject(id, x, y) {
  if(placedSymbols[id] === undefined) {
    var center = Point.random() * view.size;
    placedSymbols[id] = symbol.place(center);
  }
  return placedSymbols[id];
}

var last_frame = 0;

function onFrame(event) {
  var frame_dt = 1000.0 * (event.time - last_frame);
  last_frame = event.time;

  var now = Date.now();
  var es = EtomaEvents.current_events();
  for(var ix = 0; ix < es.length; ix++) {
    var e = es[ix];
    var obj = ensureObject(e.id);

    var dest = new Point(e.x, e.y);
    var diff = dest - obj.position;

    var dt = e.t - now + EtomaEvents.dt;
    if(dt <= frame_dt){
      obj.position = dest;
    } else {
      var delta = frame_dt / dt;
      obj.position += diff * delta;
    }
  }

  EtomaEvents.flush_to(now);
}
<commit_msg>Place at initial location<commit_after>// Create a circle shaped path with its center at the center
// of the view and a radius of 10:
var path = new Path.Circle({
  center: view.center,
  radius: 10,
  strokeColor: 'black',
  fillColor: 'blue'
  });

var symbol = new Symbol(path);

var placedSymbols = {};

function onResize(event) {
}

function ensureObject(id, center) {
  if(placedSymbols[id] === undefined) {
    if(center === undefined){
      center = Point.random() * view.size;
    }
    placedSymbols[id] = symbol.place(center);
  }
  return placedSymbols[id];
}

var last_frame = 0;

function onFrame(event) {
  var frame_dt = 1000.0 * (event.time - last_frame);
  last_frame = event.time;

  var now = Date.now();
  var es = EtomaEvents.current_events();
  for(var ix = 0; ix < es.length; ix++) {
    var e = es[ix];
    var dest = new Point(e.x, e.y);

    var obj = ensureObject(e.id, dest);
    var diff = dest - obj.position;

    var dt = e.t - now + EtomaEvents.dt;
    if(dt <= frame_dt){
      obj.position = dest;
    } else {
      var delta = frame_dt / dt;
      obj.position += diff * delta;
    }
  }

  EtomaEvents.flush_to(now);
}
<|endoftext|>


<commit_before>var _ = require('lodash');
var async = require('async');
var keystone = require('../../../../');

module.exports = function (req, res) {

	var query = req.list.model.findById(req.params.id);

	var fields = req.query.fields;
	if (req.query.basic !== undefined) {
		fields = false;
	}

	if (req.list.tracking && req.list.tracking.createdBy) {
		query.populate(req.list.tracking.createdBy);
	}

	if (req.list.tracking && req.list.tracking.updatedBy) {
		query.populate(req.list.tracking.updatedBy);
	}

	query.exec(function (err, item) {

		if (err) return res.status(500).json({ err: 'database error', detail: err });
		if (!item) return res.status(404).json({ err: 'not found', id: req.params.id });

		var tasks = [];
		var drilldown;

		/* Drilldown (optional, provided if ?drilldown=true in querystring) */
		if (req.query.drilldown === 'true' && req.list.get('drilldown')) {
			drilldown = {
				def: req.list.get('drilldown'),
				items: [],
			};

			tasks.push(function (cb) {

				// TODO: proper support for nested relationships in drilldown

				// step back through the drilldown list and load in reverse order to support nested relationships
				drilldown.def = drilldown.def.split(' ').reverse();

				async.eachSeries(drilldown.def, function (path, done) {

					var field = req.list.fields[path];

					if (!field || field.type !== 'relationship') {
						throw new Error('Drilldown for ' + req.list.key + ' is invalid: field at path ' + path + ' is not a relationship.');
					}

					var refList = field.refList;

					if (field.many) {
						if (!item.get(field.path).length) {
							return done();
						}
						refList.model.find().where('_id').in(item.get(field.path)).limit(4).exec(function (err, results) {
							if (err || !results) {
								done(err);
							}
							var more = (results.length === 4) ? results.pop() : false;
							if (results.length) {
								// drilldown.data[path] = results;
								drilldown.items.push({
									list: refList.getOptions(),
									items: _.map(results, function (i) {
										return {
											label: refList.getDocumentName(i),
											href: '/' + keystone.get('admin path') + '/' + refList.path + '/' + i.id,
										};
									}),
									more: (more) ? true : false,
								});
							}
							done();
						});
					} else {
						if (!item.get(field.path)) {
							return done();
						}
						refList.model.findById(item.get(field.path)).exec(function (err, result) {
							if (result) {
								// drilldown.data[path] = result;
								drilldown.items.push({
									list: refList.getOptions(),
									items: [{
										label: refList.getDocumentName(result),
										href: '/' + keystone.get('admin path') + '/' + refList.path + '/' + result.id,
									}],
								});
							}
							done(err);
						});
					}

				}, function (err) {
					// put the drilldown list back in the right order
					drilldown.def.reverse();
					drilldown.items.reverse();
					cb(err);
				});

			});
		}

		/* Process tasks & return */
		async.parallel(tasks, function (err) {
			if (err) {
				return res.status(500).json({
					err: 'database error',
					detail: err,
				});
			}
			res.json(_.assign(req.list.getData(item, fields), {
				drilldown: drilldown,
			}));
		});
	});
};
<commit_msg>Updating GET /api/{list}/:id to match new spec<commit_after>var _ = require('lodash');
var async = require('async');
var keystone = require('../../../../');
var listToArray = require('list-to-array');

module.exports = function (req, res) {

	var query = req.list.model.findById(req.params.id);

	var fields = req.query.fields;

	if (fields === 'false') {
		fields = false;
	}
	if (typeof fields === 'string') {
		fields = listToArray(fields);
	}
	if (fields && !Array.isArray(fields)) {
		return res.status(401).json({ error: 'fields must be undefined, a string, or an array' });
	}

	if (req.list.tracking && req.list.tracking.createdBy) {
		query.populate(req.list.tracking.createdBy);
	}

	if (req.list.tracking && req.list.tracking.updatedBy) {
		query.populate(req.list.tracking.updatedBy);
	}

	query.exec(function (err, item) {

		if (err) return res.status(500).json({ err: 'database error', detail: err });
		if (!item) return res.status(404).json({ err: 'not found', id: req.params.id });

		var tasks = [];
		var drilldown;

		/* Drilldown (optional, provided if ?drilldown=true in querystring) */
		if (req.query.drilldown === 'true' && req.list.get('drilldown')) {
			drilldown = {
				def: req.list.get('drilldown'),
				items: [],
			};

			tasks.push(function (cb) {

				// TODO: proper support for nested relationships in drilldown

				// step back through the drilldown list and load in reverse order to support nested relationships
				drilldown.def = drilldown.def.split(' ').reverse();

				async.eachSeries(drilldown.def, function (path, done) {

					var field = req.list.fields[path];

					if (!field || field.type !== 'relationship') {
						throw new Error('Drilldown for ' + req.list.key + ' is invalid: field at path ' + path + ' is not a relationship.');
					}

					var refList = field.refList;

					if (field.many) {
						if (!item.get(field.path).length) {
							return done();
						}
						refList.model.find().where('_id').in(item.get(field.path)).limit(4).exec(function (err, results) {
							if (err || !results) {
								done(err);
							}
							var more = (results.length === 4) ? results.pop() : false;
							if (results.length) {
								// drilldown.data[path] = results;
								drilldown.items.push({
									list: refList.getOptions(),
									items: _.map(results, function (i) {
										return {
											label: refList.getDocumentName(i),
											href: '/' + keystone.get('admin path') + '/' + refList.path + '/' + i.id,
										};
									}),
									more: (more) ? true : false,
								});
							}
							done();
						});
					} else {
						if (!item.get(field.path)) {
							return done();
						}
						refList.model.findById(item.get(field.path)).exec(function (err, result) {
							if (result) {
								// drilldown.data[path] = result;
								drilldown.items.push({
									list: refList.getOptions(),
									items: [{
										label: refList.getDocumentName(result),
										href: '/' + keystone.get('admin path') + '/' + refList.path + '/' + result.id,
									}],
								});
							}
							done(err);
						});
					}

				}, function (err) {
					// put the drilldown list back in the right order
					drilldown.def.reverse();
					drilldown.items.reverse();
					cb(err);
				});

			});
		}

		/* Process tasks & return */
		async.parallel(tasks, function (err) {
			if (err) {
				return res.status(500).json({
					err: 'database error',
					detail: err,
				});
			}
			res.json(_.assign(req.list.getData(item, fields), {
				drilldown: drilldown,
			}));
		});
	});
};
<|endoftext|>


<commit_before>'use strict';

var tap = require('tap');
var rewire = require('rewire');
var _ = require('lodash');
var eol = require('os').EOL;
var Pattern = require('../src/lib/object_factory').Pattern;
var extend = require('util')._extend;
var uiModule = rewire('../src/lib/ui_builder');
var path = require('path');
var config = require('./util/patternlab-config.json');

var engineLoader = require('../src/lib/pattern_engines');
engineLoader.loadAllEngines(config);

//set up a global mocks - we don't want to be writing/rendering any files right now
var fsMock = {
  outputFileSync: function(path, data, cb) {},
};

var renderMock = function(template, data, partials) {
  return Promise.resolve('');
};
var buildFooterMock = function(patternlab, patternPartial) {
  return Promise.resolve('');
};

//set our mocks in place of usual require()
uiModule.__set__({
  fs: fsMock,
  render: renderMock,
  buildFooter: buildFooterMock,
});

var ui = uiModule();

function createFakePatternLab(customProps) {
  var pl = {
    config: {
      paths: {
        source: {
          patterns: './test/files/_patterns',
        },
        public: {
          patterns: './test/output',
        },
      },
      styleGuideExcludes: ['templates'],
      logLevel: 'quiet',
      outputFileSuffixes: {
        rendered: '.rendered',
        rawTemplate: '',
        markupOnly: '.markup-only',
      },
    },
    data: {},
  };
  return extend(pl, customProps);
}

tap.test(
  'isPatternExcluded - returns true when pattern filename starts with underscore',
  function(test) {
    //arrange
    var patternlab = createFakePatternLab({});
    var pattern = new Pattern('00-test/_ignored-pattern.mustache');

    //act
    var result = ui.isPatternExcluded(pattern, patternlab);

    //assert
    test.equals(result, true);
    test.end();
  }
);

tap.test(
  'isPatternExcluded - returns true when pattern is defaultPattern',
  function(test) {
    //arrange
    var patternlab = createFakePatternLab({});
    var pattern = new Pattern('00-test/foo.mustache');
    patternlab.config.defaultPattern = 'test-foo';

    //act
    var result = ui.isPatternExcluded(pattern, patternlab);

    //assert
    test.equals(result, true);
    test.end();
  }
);

tap.test(
  'isPatternExcluded - returns true when pattern within underscored directory - top level',
  function(test) {
    //arrange
    var patternlab = createFakePatternLab({});
    var pattern = Pattern.createEmpty({
      relPath:
        path.sep +
        '_hidden' +
        path.sep +
        'patternsubtype' +
        path.sep +
        'foo.mustache',
      isPattern: true,
      fileName: 'foo.mustache',
      patternPartial: 'hidden-foo',
    });

    //act
    var result = ui.isPatternExcluded(pattern, patternlab);

    //assert
    test.equals(result, true);
    test.end();
  }
);

tap.test(
  'isPatternExcluded - returns true when pattern within underscored directory - subtype level',
  function(test) {
    //arrange
    var patternlab = createFakePatternLab({});
    var pattern = Pattern.createEmpty({
      relPath:
        'shown' + path.sep + '_patternsubtype' + path.sep + 'foo.mustache',
      isPattern: true,
      fileName: 'foo.mustache',
      patternPartial: 'shown-foo',
    });

    //act
    var result = ui.isPatternExcluded(pattern, patternlab);

    //assert
    test.equals(result, true);
    test.end();
  }
);

tap.test('groupPatterns - creates pattern groups correctly', function(test) {
  //arrange
  var patternlab = createFakePatternLab({
    patterns: [],
    patternGroups: {},
    subtypePatterns: {},
  });

  patternlab.patterns.push(
    new Pattern('00-test/bar.mustache'),
    new Pattern('00-test/foo.mustache'),
    new Pattern('patternType1/patternSubType1/blue.mustache'),
    new Pattern('patternType1/patternSubType1/red.mustache'),
    new Pattern('patternType1/patternSubType1/yellow.mustache'),
    new Pattern('patternType1/patternSubType2/black.mustache'),
    new Pattern('patternType1/patternSubType2/grey.mustache'),
    new Pattern('patternType1/patternSubType2/white.mustache')
  );
  ui.resetUIBuilderState(patternlab);

  //act
  var result = ui.groupPatterns(patternlab);

  test.equals(
    result.patternGroups.patternType1.patternSubType1.blue.patternPartial,
    'patternType1-blue'
  );
  test.equals(
    result.patternGroups.patternType1.patternSubType1.red.patternPartial,
    'patternType1-red'
  );
  test.equals(
    result.patternGroups.patternType1.patternSubType1.yellow.patternPartial,
    'patternType1-yellow'
  );
  test.equals(
    result.patternGroups.patternType1.patternSubType2.black.patternPartial,
    'patternType1-black'
  );
  test.equals(
    result.patternGroups.patternType1.patternSubType2.grey.patternPartial,
    'patternType1-grey'
  );
  test.equals(
    result.patternGroups.patternType1.patternSubType2.white.patternPartial,
    'patternType1-white'
  );

  test.equals(
    patternlab.patternTypes[0].patternItems[0].patternPartial,
    'test-bar',
    'first pattern item should be test-bar'
  );
  test.equals(
    patternlab.patternTypes[0].patternItems[1].patternPartial,
    'test-foo',
    'second pattern item should be test-foo'
  );

  //todo: patternlab.patternTypes[0].patternItems[1] looks malformed

  test.end();
});

tap.test('groupPatterns - orders patterns when provided from md', function(
  test
) {
  //arrange
  var patternlab = createFakePatternLab({
    patterns: [],
    patternGroups: {},
    subtypePatterns: {},
  });

  patternlab.patterns.push(
    new Pattern('patternType1/patternSubType1/blue.mustache'),
    new Pattern('patternType1/patternSubType1/red.mustache'),
    new Pattern('patternType1/patternSubType1/yellow.mustache')
  );
  ui.resetUIBuilderState(patternlab);

  patternlab.patterns[1].order = 1;

  //act
  ui.groupPatterns(patternlab);

  let patternType = _.find(patternlab.patternTypes, [
    'patternType',
    'patternType1',
  ]);
  let patternSubType = _.find(patternType.patternTypeItems, [
    'patternSubtype',
    'patternSubType1',
  ]);
  var items = patternSubType.patternSubtypeItems;

  //zero is viewall
  test.equals(items[1].patternPartial, 'patternType1-red');
  test.equals(items[2].patternPartial, 'patternType1-blue');
  test.equals(items[3].patternPartial, 'patternType1-yellow');

  test.end();
});

tap.test(
  'groupPatterns - retains pattern order from name when order provided from md is malformed',
  function(test) {
    //arrange
    var patternlab = createFakePatternLab({
      patterns: [],
      patternGroups: {},
      subtypePatterns: {},
    });

    patternlab.patterns.push(
      new Pattern('patternType1/patternSubType1/blue.mustache'),
      new Pattern('patternType1/patternSubType1/red.mustache'),
      new Pattern('patternType1/patternSubType1/yellow.mustache')
    );
    ui.resetUIBuilderState(patternlab);

    patternlab.patterns[1].order = 'notanumber!';

    //act
    ui.groupPatterns(patternlab);

    let patternType = _.find(patternlab.patternTypes, [
      'patternType',
      'patternType1',
    ]);
    let patternSubType = _.find(patternType.patternTypeItems, [
      'patternSubtype',
      'patternSubType1',
    ]);
    var items = patternSubType.patternSubtypeItems;

    //zero is viewall
    test.equals(items[1].patternPartial, 'patternType1-blue');
    test.equals(items[2].patternPartial, 'patternType1-red');
    test.equals(items[3].patternPartial, 'patternType1-yellow');

    test.end();
  }
);

tap.test(
  'groupPatterns - sorts viewall subtype pattern to the beginning',
  function(test) {
    //arrange
    var patternlab = createFakePatternLab({
      patterns: [],
      patternGroups: {},
      subtypePatterns: {},
    });

    patternlab.patterns.push(
      new Pattern('patternType1/patternSubType1/blue.mustache'),
      new Pattern('patternType1/patternSubType1/red.mustache'),
      new Pattern('patternType1/patternSubType1/yellow.mustache')
    );
    ui.resetUIBuilderState(patternlab);

    patternlab.patterns[0].order = 1;
    patternlab.patterns[1].order = 3;
    patternlab.patterns[2].order = 2;

    //act
    ui.groupPatterns(patternlab);

    let patternType = _.find(patternlab.patternTypes, [
      'patternType',
      'patternType1',
    ]);
    let patternSubType = _.find(patternType.patternTypeItems, [
      'patternSubtype',
      'patternSubType1',
    ]);
    var items = patternSubType.patternSubtypeItems;

    //zero is viewall
    test.equals(
      items[0].patternPartial,
      'viewall-patternType1-patternSubType1'
    );
    test.equals(items[1].patternPartial, 'patternType1-blue');
    test.equals(items[2].patternPartial, 'patternType1-yellow');
    test.equals(items[3].patternPartial, 'patternType1-red');

    test.end();
  }
);

tap.test(
  'groupPatterns - creates documentation patterns for each type and subtype if not exists',
  function(test) {
    //arrange
    var patternlab = createFakePatternLab({
      patterns: [],
      patternGroups: {},
      subtypePatterns: {},
    });

    patternlab.patterns.push(
      new Pattern('00-test/foo.mustache'),
      new Pattern('00-test/bar.mustache'),
      new Pattern('patternType1/patternSubType1/blue.mustache'),
      new Pattern('patternType1/patternSubType1/red.mustache'),
      new Pattern('patternType1/patternSubType1/yellow.mustache'),
      new Pattern('patternType1/patternSubType2/black.mustache'),
      new Pattern('patternType1/patternSubType2/grey.mustache'),
      new Pattern('patternType1/patternSubType2/white.mustache')
    );
    ui.resetUIBuilderState(patternlab);

    //act
    var result = ui.groupPatterns(patternlab);

    //assert
    test.equals(
      result.patternGroups.patternType1.patternSubType1[
        'viewall-patternType1-patternSubType1'
      ].patternPartial,
      'viewall-patternType1-patternSubType1'
    );
    test.equals(
      result.patternGroups.patternType1.patternSubType2[
        'viewall-patternType1-patternSubType2'
      ].patternPartial,
      'viewall-patternType1-patternSubType2'
    );

    test.end();
  }
);

tap.test(
  'groupPatterns - adds each pattern to the patternPaths object',
  function(test) {
    //arrange
    var patternlab = createFakePatternLab({
      patterns: [],
      patternGroups: {},
      subtypePatterns: {},
    });

    patternlab.patterns.push(
      new Pattern('00-test/foo.mustache'),
      new Pattern('00-test/bar.mustache'),
      new Pattern('patternType1/patternSubType1/blue.mustache'),
      new Pattern('patternType1/patternSubType1/red.mustache'),
      new Pattern('patternType1/patternSubType1/yellow.mustache'),
      new Pattern('patternType1/patternSubType2/black.mustache'),
      new Pattern('patternType1/patternSubType2/grey.mustache'),
      new Pattern('patternType1/patternSubType2/white.mustache')
    );
    ui.resetUIBuilderState(patternlab);

    //act
    var result = ui.groupPatterns(patternlab);

    //assert
    test.equals(patternlab.patternPaths['test']['foo'], '00-test-foo');
    test.equals(patternlab.patternPaths['test']['bar'], '00-test-bar');
    test.equals(
      patternlab.patternPaths['patternType1']['blue'],
      'patternType1-patternSubType1-blue'
    );
    test.equals(
      patternlab.patternPaths['patternType1']['red'],
      'patternType1-patternSubType1-red'
    );
    test.equals(
      patternlab.patternPaths['patternType1']['yellow'],
      'patternType1-patternSubType1-yellow'
    );
    test.equals(
      patternlab.patternPaths['patternType1']['black'],
      'patternType1-patternSubType2-black'
    );
    test.equals(
      patternlab.patternPaths['patternType1']['grey'],
      'patternType1-patternSubType2-grey'
    );
    test.equals(
      patternlab.patternPaths['patternType1']['white'],
      'patternType1-patternSubType2-white'
    );

    test.end();
  }
);

tap.test(
  'groupPatterns - adds each pattern to the view all paths object',
  function(test) {
    //arrange
    var patternlab = createFakePatternLab({
      patterns: [],
      patternGroups: {},
      subtypePatterns: {},
    });

    patternlab.patterns.push(
      new Pattern('00-test/foo.mustache'),
      new Pattern('00-test/bar.mustache'),
      new Pattern('patternType1/patternSubType1/blue.mustache'),
      new Pattern('patternType1/patternSubType1/red.mustache'),
      new Pattern('patternType1/patternSubType1/yellow.mustache'),
      new Pattern('patternType1/patternSubType2/black.mustache'),
      new Pattern('patternType1/patternSubType2/grey.mustache'),
      new Pattern('patternType1/patternSubType2/white.mustache')
    );
    ui.resetUIBuilderState(patternlab);

    //act
    var result = ui.groupPatterns(patternlab);

    //assert
    test.equals('todo', 'todo');

    test.end();
  }
);

tap.test('resetUIBuilderState - reset global objects', function(test) {
  //arrange
  var patternlab = createFakePatternLab({
    patternPaths: { foo: 1 },
    viewAllPaths: { bar: 2 },
    patternTypes: ['baz'],
  });

  //act
  ui.resetUIBuilderState(patternlab);

  //assert
  test.equals(patternlab.patternPaths.foo, undefined);
  test.equals(patternlab.viewAllPaths.bar, undefined);
  test.equals(patternlab.patternTypes.length, 0);

  test.end();
});

tap.test(
  'buildViewAllPages - adds viewall page for each type and subtype',
  function(test) {
    //arrange
    const mainPageHeadHtml = '<head></head>';
    const patternlab = createFakePatternLab({
      patterns: [],
      patternGroups: {},
      subtypePatterns: {},
      footer: {},
      userFoot: {},
      cacheBuster: 1234,
    });

    patternlab.patterns.push(
      //this flat pattern is found and causes trouble for the rest of the crew
      new Pattern('00-test/foo.mustache'),
      new Pattern('patternType1/patternSubType1/blue.mustache'),
      new Pattern('patternType1/patternSubType1/red.mustache'),
      new Pattern('patternType1/patternSubType1/yellow.mustache'),
      new Pattern('patternType1/patternSubType2/black.mustache'),
      new Pattern('patternType1/patternSubType2/grey.mustache'),
      new Pattern('patternType1/patternSubType2/white.mustache')
    );
    ui.resetUIBuilderState(patternlab);

    const styleguidePatterns = ui.groupPatterns(patternlab);

    //act
    ui
      .buildViewAllPages(mainPageHeadHtml, patternlab, styleguidePatterns)
      .then(allPatterns => {
        //assert
        //this was a nuanced one. buildViewAllPages() had return false; statements
        //within _.forOwn(...) loops, causing premature termination of the entire loop
        //when what was intended was a continue
        //we expect 8 here because:
        //  - foo.mustache is flat and therefore does not have a viewall page
        //  - the colors.mustache files make 6
        //  - patternSubType1 and patternSubType2 make 8
        //while most of that heavy lifting occurs inside groupPatterns and not buildViewAllPages,
        //it's important to ensure that this method does not get prematurely terminated
        //we choose to do that by checking it's return number of patterns

        //todo: this workaround matches the code at the moment
        const uniquePatterns = _.uniq(
          _.flatMapDeep(allPatterns, pattern => {
            return pattern;
          })
        );

        test.equals(
          uniquePatterns.length,
          8,
          '2 viewall pages should be added'
        );

        test.end();
      });
  }
);
<commit_msg>fix(uikits): fix ui_builder_tests<commit_after>'use strict';

var tap = require('tap');
var rewire = require('rewire');
var _ = require('lodash');
var eol = require('os').EOL;
var Pattern = require('../src/lib/object_factory').Pattern;
var extend = require('util')._extend;
var uiModule = rewire('../src/lib/ui_builder');
var path = require('path');
var config = require('./util/patternlab-config.json');

var engineLoader = require('../src/lib/pattern_engines');
engineLoader.loadAllEngines(config);

//set up a global mocks - we don't want to be writing/rendering any files right now
var fsMock = {
  outputFileSync: function(path, data, cb) {},
  outputFile: function(path, data, cb) {},
};

var renderMock = function(template, data, partials) {
  return Promise.resolve('');
};
var buildFooterMock = function(patternlab, patternPartial) {
  return Promise.resolve('');
};

//set our mocks in place of usual require()
uiModule.__set__({
  fs: fsMock,
  render: renderMock,
  buildFooter: buildFooterMock,
});

var ui = uiModule();

function createFakePatternLab(customProps) {
  var pl = {
    config: {
      paths: {
        source: {
          patterns: './test/files/_patterns',
        },
        public: {
          patterns: '',
        },
      },
      styleGuideExcludes: ['templates'],
      logLevel: 'quiet',
      outputFileSuffixes: {
        rendered: '.rendered',
        rawTemplate: '',
        markupOnly: '.markup-only',
      },
    },
    data: {},
    uikits: {
      'uikit-workshop': {
        name: 'workshop',
        modulePath: '',
        outputDir: 'test/output',
      },
    },
  };
  return extend(pl, customProps);
}

tap.test(
  'isPatternExcluded - returns true when pattern filename starts with underscore',
  function(test) {
    //arrange
    var patternlab = createFakePatternLab({});
    var pattern = new Pattern('00-test/_ignored-pattern.mustache');

    //act
    var result = ui.isPatternExcluded(pattern, patternlab);

    //assert
    test.equals(result, true);
    test.end();
  }
);

tap.test(
  'isPatternExcluded - returns true when pattern is defaultPattern',
  function(test) {
    //arrange
    var patternlab = createFakePatternLab({});
    var pattern = new Pattern('00-test/foo.mustache');
    patternlab.config.defaultPattern = 'test-foo';

    //act
    var result = ui.isPatternExcluded(pattern, patternlab);

    //assert
    test.equals(result, true);
    test.end();
  }
);

tap.test(
  'isPatternExcluded - returns true when pattern within underscored directory - top level',
  function(test) {
    //arrange
    var patternlab = createFakePatternLab({});
    var pattern = Pattern.createEmpty({
      relPath:
        path.sep +
        '_hidden' +
        path.sep +
        'patternsubtype' +
        path.sep +
        'foo.mustache',
      isPattern: true,
      fileName: 'foo.mustache',
      patternPartial: 'hidden-foo',
    });

    //act
    var result = ui.isPatternExcluded(pattern, patternlab);

    //assert
    test.equals(result, true);
    test.end();
  }
);

tap.test(
  'isPatternExcluded - returns true when pattern within underscored directory - subtype level',
  function(test) {
    //arrange
    var patternlab = createFakePatternLab({});
    var pattern = Pattern.createEmpty({
      relPath:
        'shown' + path.sep + '_patternsubtype' + path.sep + 'foo.mustache',
      isPattern: true,
      fileName: 'foo.mustache',
      patternPartial: 'shown-foo',
    });

    //act
    var result = ui.isPatternExcluded(pattern, patternlab);

    //assert
    test.equals(result, true);
    test.end();
  }
);

tap.test('groupPatterns - creates pattern groups correctly', function(test) {
  //arrange
  var patternlab = createFakePatternLab({
    patterns: [],
    patternGroups: {},
    subtypePatterns: {},
  });

  patternlab.patterns.push(
    new Pattern('00-test/bar.mustache'),
    new Pattern('00-test/foo.mustache'),
    new Pattern('patternType1/patternSubType1/blue.mustache'),
    new Pattern('patternType1/patternSubType1/red.mustache'),
    new Pattern('patternType1/patternSubType1/yellow.mustache'),
    new Pattern('patternType1/patternSubType2/black.mustache'),
    new Pattern('patternType1/patternSubType2/grey.mustache'),
    new Pattern('patternType1/patternSubType2/white.mustache')
  );
  ui.resetUIBuilderState(patternlab);

  //act
  var result = ui.groupPatterns(patternlab);

  test.equals(
    result.patternGroups.patternType1.patternSubType1.blue.patternPartial,
    'patternType1-blue'
  );
  test.equals(
    result.patternGroups.patternType1.patternSubType1.red.patternPartial,
    'patternType1-red'
  );
  test.equals(
    result.patternGroups.patternType1.patternSubType1.yellow.patternPartial,
    'patternType1-yellow'
  );
  test.equals(
    result.patternGroups.patternType1.patternSubType2.black.patternPartial,
    'patternType1-black'
  );
  test.equals(
    result.patternGroups.patternType1.patternSubType2.grey.patternPartial,
    'patternType1-grey'
  );
  test.equals(
    result.patternGroups.patternType1.patternSubType2.white.patternPartial,
    'patternType1-white'
  );

  test.equals(
    patternlab.patternTypes[0].patternItems[0].patternPartial,
    'test-bar',
    'first pattern item should be test-bar'
  );
  test.equals(
    patternlab.patternTypes[0].patternItems[1].patternPartial,
    'test-foo',
    'second pattern item should be test-foo'
  );

  //todo: patternlab.patternTypes[0].patternItems[1] looks malformed

  test.end();
});

tap.test('groupPatterns - orders patterns when provided from md', function(
  test
) {
  //arrange
  var patternlab = createFakePatternLab({
    patterns: [],
    patternGroups: {},
    subtypePatterns: {},
  });

  patternlab.patterns.push(
    new Pattern('patternType1/patternSubType1/blue.mustache'),
    new Pattern('patternType1/patternSubType1/red.mustache'),
    new Pattern('patternType1/patternSubType1/yellow.mustache')
  );
  ui.resetUIBuilderState(patternlab);

  patternlab.patterns[1].order = 1;

  //act
  ui.groupPatterns(patternlab);

  let patternType = _.find(patternlab.patternTypes, [
    'patternType',
    'patternType1',
  ]);
  let patternSubType = _.find(patternType.patternTypeItems, [
    'patternSubtype',
    'patternSubType1',
  ]);
  var items = patternSubType.patternSubtypeItems;

  //zero is viewall
  test.equals(items[1].patternPartial, 'patternType1-red');
  test.equals(items[2].patternPartial, 'patternType1-blue');
  test.equals(items[3].patternPartial, 'patternType1-yellow');

  test.end();
});

tap.test(
  'groupPatterns - retains pattern order from name when order provided from md is malformed',
  function(test) {
    //arrange
    var patternlab = createFakePatternLab({
      patterns: [],
      patternGroups: {},
      subtypePatterns: {},
    });

    patternlab.patterns.push(
      new Pattern('patternType1/patternSubType1/blue.mustache'),
      new Pattern('patternType1/patternSubType1/red.mustache'),
      new Pattern('patternType1/patternSubType1/yellow.mustache')
    );
    ui.resetUIBuilderState(patternlab);

    patternlab.patterns[1].order = 'notanumber!';

    //act
    ui.groupPatterns(patternlab);

    let patternType = _.find(patternlab.patternTypes, [
      'patternType',
      'patternType1',
    ]);
    let patternSubType = _.find(patternType.patternTypeItems, [
      'patternSubtype',
      'patternSubType1',
    ]);
    var items = patternSubType.patternSubtypeItems;

    //zero is viewall
    test.equals(items[1].patternPartial, 'patternType1-blue');
    test.equals(items[2].patternPartial, 'patternType1-red');
    test.equals(items[3].patternPartial, 'patternType1-yellow');

    test.end();
  }
);

tap.test(
  'groupPatterns - sorts viewall subtype pattern to the beginning',
  function(test) {
    //arrange
    var patternlab = createFakePatternLab({
      patterns: [],
      patternGroups: {},
      subtypePatterns: {},
    });

    patternlab.patterns.push(
      new Pattern('patternType1/patternSubType1/blue.mustache'),
      new Pattern('patternType1/patternSubType1/red.mustache'),
      new Pattern('patternType1/patternSubType1/yellow.mustache')
    );
    ui.resetUIBuilderState(patternlab);

    patternlab.patterns[0].order = 1;
    patternlab.patterns[1].order = 3;
    patternlab.patterns[2].order = 2;

    //act
    ui.groupPatterns(patternlab);

    let patternType = _.find(patternlab.patternTypes, [
      'patternType',
      'patternType1',
    ]);
    let patternSubType = _.find(patternType.patternTypeItems, [
      'patternSubtype',
      'patternSubType1',
    ]);
    var items = patternSubType.patternSubtypeItems;

    //zero is viewall
    test.equals(
      items[0].patternPartial,
      'viewall-patternType1-patternSubType1'
    );
    test.equals(items[1].patternPartial, 'patternType1-blue');
    test.equals(items[2].patternPartial, 'patternType1-yellow');
    test.equals(items[3].patternPartial, 'patternType1-red');

    test.end();
  }
);

tap.test(
  'groupPatterns - creates documentation patterns for each type and subtype if not exists',
  function(test) {
    //arrange
    var patternlab = createFakePatternLab({
      patterns: [],
      patternGroups: {},
      subtypePatterns: {},
    });

    patternlab.patterns.push(
      new Pattern('00-test/foo.mustache'),
      new Pattern('00-test/bar.mustache'),
      new Pattern('patternType1/patternSubType1/blue.mustache'),
      new Pattern('patternType1/patternSubType1/red.mustache'),
      new Pattern('patternType1/patternSubType1/yellow.mustache'),
      new Pattern('patternType1/patternSubType2/black.mustache'),
      new Pattern('patternType1/patternSubType2/grey.mustache'),
      new Pattern('patternType1/patternSubType2/white.mustache')
    );
    ui.resetUIBuilderState(patternlab);

    //act
    var result = ui.groupPatterns(patternlab);

    //assert
    test.equals(
      result.patternGroups.patternType1.patternSubType1[
        'viewall-patternType1-patternSubType1'
      ].patternPartial,
      'viewall-patternType1-patternSubType1'
    );
    test.equals(
      result.patternGroups.patternType1.patternSubType2[
        'viewall-patternType1-patternSubType2'
      ].patternPartial,
      'viewall-patternType1-patternSubType2'
    );

    test.end();
  }
);

tap.test(
  'groupPatterns - adds each pattern to the patternPaths object',
  function(test) {
    //arrange
    var patternlab = createFakePatternLab({
      patterns: [],
      patternGroups: {},
      subtypePatterns: {},
    });

    patternlab.patterns.push(
      new Pattern('00-test/foo.mustache'),
      new Pattern('00-test/bar.mustache'),
      new Pattern('patternType1/patternSubType1/blue.mustache'),
      new Pattern('patternType1/patternSubType1/red.mustache'),
      new Pattern('patternType1/patternSubType1/yellow.mustache'),
      new Pattern('patternType1/patternSubType2/black.mustache'),
      new Pattern('patternType1/patternSubType2/grey.mustache'),
      new Pattern('patternType1/patternSubType2/white.mustache')
    );
    ui.resetUIBuilderState(patternlab);

    //act
    var result = ui.groupPatterns(patternlab);

    //assert
    test.equals(patternlab.patternPaths['test']['foo'], '00-test-foo');
    test.equals(patternlab.patternPaths['test']['bar'], '00-test-bar');
    test.equals(
      patternlab.patternPaths['patternType1']['blue'],
      'patternType1-patternSubType1-blue'
    );
    test.equals(
      patternlab.patternPaths['patternType1']['red'],
      'patternType1-patternSubType1-red'
    );
    test.equals(
      patternlab.patternPaths['patternType1']['yellow'],
      'patternType1-patternSubType1-yellow'
    );
    test.equals(
      patternlab.patternPaths['patternType1']['black'],
      'patternType1-patternSubType2-black'
    );
    test.equals(
      patternlab.patternPaths['patternType1']['grey'],
      'patternType1-patternSubType2-grey'
    );
    test.equals(
      patternlab.patternPaths['patternType1']['white'],
      'patternType1-patternSubType2-white'
    );

    test.end();
  }
);

tap.test(
  'groupPatterns - adds each pattern to the view all paths object',
  function(test) {
    //arrange
    var patternlab = createFakePatternLab({
      patterns: [],
      patternGroups: {},
      subtypePatterns: {},
    });

    patternlab.patterns.push(
      new Pattern('00-test/foo.mustache'),
      new Pattern('00-test/bar.mustache'),
      new Pattern('patternType1/patternSubType1/blue.mustache'),
      new Pattern('patternType1/patternSubType1/red.mustache'),
      new Pattern('patternType1/patternSubType1/yellow.mustache'),
      new Pattern('patternType1/patternSubType2/black.mustache'),
      new Pattern('patternType1/patternSubType2/grey.mustache'),
      new Pattern('patternType1/patternSubType2/white.mustache')
    );
    ui.resetUIBuilderState(patternlab);

    //act
    var result = ui.groupPatterns(patternlab);

    //assert
    test.equals('todo', 'todo');

    test.end();
  }
);

tap.test('resetUIBuilderState - reset global objects', function(test) {
  //arrange
  var patternlab = createFakePatternLab({
    patternPaths: { foo: 1 },
    viewAllPaths: { bar: 2 },
    patternTypes: ['baz'],
  });

  //act
  ui.resetUIBuilderState(patternlab);

  //assert
  test.equals(patternlab.patternPaths.foo, undefined);
  test.equals(patternlab.viewAllPaths.bar, undefined);
  test.equals(patternlab.patternTypes.length, 0);

  test.end();
});

tap.test(
  'buildViewAllPages - adds viewall page for each type and subtype',
  function(test) {
    //arrange
    const mainPageHeadHtml = '<head></head>';
    const patternlab = createFakePatternLab({
      patterns: [],
      patternGroups: {},
      subtypePatterns: {},
      footer: {},
      userFoot: {},
      cacheBuster: 1234,
    });

    patternlab.patterns.push(
      //this flat pattern is found and causes trouble for the rest of the crew
      new Pattern('00-test/foo.mustache'),
      new Pattern('patternType1/patternSubType1/blue.mustache'),
      new Pattern('patternType1/patternSubType1/red.mustache'),
      new Pattern('patternType1/patternSubType1/yellow.mustache'),
      new Pattern('patternType1/patternSubType2/black.mustache'),
      new Pattern('patternType1/patternSubType2/grey.mustache'),
      new Pattern('patternType1/patternSubType2/white.mustache')
    );
    ui.resetUIBuilderState(patternlab);

    const styleguidePatterns = ui.groupPatterns(patternlab);

    const uikit = patternlab.uikits['uikit-workshop'];

    //act
    ui
      .buildViewAllPages(
        mainPageHeadHtml,
        patternlab,
        styleguidePatterns,
        uikit
      )
      .then(allPatterns => {
        //assert
        //this was a nuanced one. buildViewAllPages() had return false; statements
        //within _.forOwn(...) loops, causing premature termination of the entire loop
        //when what was intended was a continue
        //we expect 8 here because:
        //  - foo.mustache is flat and therefore does not have a viewall page
        //  - the colors.mustache files make 6
        //  - patternSubType1 and patternSubType2 make 8
        //while most of that heavy lifting occurs inside groupPatterns and not buildViewAllPages,
        //it's important to ensure that this method does not get prematurely terminated
        //we choose to do that by checking it's return number of patterns

        //todo: this workaround matches the code at the moment
        const uniquePatterns = _.uniq(
          _.flatMapDeep(allPatterns, pattern => {
            return pattern;
          })
        );

        test.equals(
          uniquePatterns.length,
          8,
          '2 viewall pages should be added'
        );

        test.end();
      });
  }
);
<|endoftext|>


<commit_before>var LimitsWS = (function(){
    "use strict";

    function limitsHandler(response){
        var limits = response.get_limits;
        LimitsUI.fillLimitsTable(limits);

        var withdrawal_limit = document.getElementById("withdrawal-limit");
        var already_withdraw = document.getElementById("already-withdraw");
        var withdrawal_limit_aggregate = document.getElementById("withdrawal-limit-aggregate");

        if(limits['lifetime_limit'] === 99999999) {
            withdrawal_limit.textContent = Content.localize().textAuthenticatedWithdrawal;
        } else if(limits['num_of_days_limit'] === limits['lifetime_limit']) {
            withdrawal_limit.textContent = Content.localize().textWithdrawalLimits.replace('[_1]', TUser.get().currency || page.client.get_storage_value('currencies')).replace('[_2]', addComma(limits['num_of_days_limit']));
            already_withdraw.textContent = Content.localize().textWithrawalAmount.replace('[_1]', addComma(limits["withdrawal_since_inception_monetary"])) + '.';
        } else {
            withdrawal_limit.textContent = Content.localize().textDayWithdrawalLimit.replace('[_1]', limits['num_of_days']).replace('[_2]', addComma(limits['num_of_days_limit']));
            already_withdraw.textContent = Content.localize().textWithrawalAmount.replace('[_1]', limits['withdrawal_for_x_days_monetary']) + " " + Content.localize().textAggregateOverLast + " " + limits['num_of_days'] + " " + Content.localize().textDurationDays;
            if(limits["lifetime_limit"] < 99999999) {
                withdrawal_limit_aggregate.textContent = Content.localize().textWithdrawalForEntireDuration.replace('[_1]', addComma(limits["lifetime_limit"]));
                document.getElementById("already-withdraw-aggregate").textContent = Content.localize().textWithrawalAmount.replace('[_1]', addComma(limits["withdrawal_since_inception_monetary"])) + " " + Content.localize().textInAggregateOverLifetime;
            }
            if(limits['remainder'] === 0) {
                withdrawal_limit_aggregate.textContent = Content.localize().textNotAllowedToWithdraw;
            } else if (limits['remainder'] !== 0) {
                withdrawal_limit_aggregate.textContent = Content.localize().textCurrentMaxWithdrawal.replace('[_1]', addComma(limits['remainder']));
            }

        }
    }

    function limitsError(){
        document.getElementById('limits-title').setAttribute('style', 'display:none');
        document.getElementsByClassName('notice-msg')[0].innerHTML = Content.localize().textFeatureUnavailable;
        document.getElementById('client_message').setAttribute('style', 'display:block');
    }

    function initTable(){
        document.getElementById('client_message').setAttribute('style', 'display:none');
        LimitsUI.clearTableContent();
    }

    return {
        limitsHandler: limitsHandler,
        limitsError: limitsError,
        clean: initTable
    };
}());
<commit_msg>Fix fully authenticated condition<commit_after>var LimitsWS = (function(){
    "use strict";

    function limitsHandler(response){
        var limits = response.get_limits;
        LimitsUI.fillLimitsTable(limits);

        var withdrawal_limit = document.getElementById("withdrawal-limit");
        var already_withdraw = document.getElementById("already-withdraw");
        var withdrawal_limit_aggregate = document.getElementById("withdrawal-limit-aggregate");

        if(limits['lifetime_limit'] === 99999999 && limits['num_of_days_limit'] === 99999999) {
            withdrawal_limit.textContent = Content.localize().textAuthenticatedWithdrawal;
        } else if(limits['num_of_days_limit'] === limits['lifetime_limit']) {
            withdrawal_limit.textContent = Content.localize().textWithdrawalLimits.replace('[_1]', TUser.get().currency || page.client.get_storage_value('currencies')).replace('[_2]', addComma(limits['num_of_days_limit']));
            already_withdraw.textContent = Content.localize().textWithrawalAmount.replace('[_1]', addComma(limits["withdrawal_since_inception_monetary"])) + '.';
        } else {
            withdrawal_limit.textContent = Content.localize().textDayWithdrawalLimit.replace('[_1]', limits['num_of_days']).replace('[_2]', addComma(limits['num_of_days_limit']));
            already_withdraw.textContent = Content.localize().textWithrawalAmount.replace('[_1]', limits['withdrawal_for_x_days_monetary']) + " " + Content.localize().textAggregateOverLast + " " + limits['num_of_days'] + " " + Content.localize().textDurationDays;
            if(limits["lifetime_limit"] < 99999999) {
                withdrawal_limit_aggregate.textContent = Content.localize().textWithdrawalForEntireDuration.replace('[_1]', addComma(limits["lifetime_limit"]));
                document.getElementById("already-withdraw-aggregate").textContent = Content.localize().textWithrawalAmount.replace('[_1]', addComma(limits["withdrawal_since_inception_monetary"])) + " " + Content.localize().textInAggregateOverLifetime;
            }
            if(limits['remainder'] === 0) {
                withdrawal_limit_aggregate.textContent = Content.localize().textNotAllowedToWithdraw;
            } else if (limits['remainder'] !== 0) {
                withdrawal_limit_aggregate.textContent = Content.localize().textCurrentMaxWithdrawal.replace('[_1]', addComma(limits['remainder']));
            }

        }
    }

    function limitsError(){
        document.getElementById('limits-title').setAttribute('style', 'display:none');
        document.getElementsByClassName('notice-msg')[0].innerHTML = Content.localize().textFeatureUnavailable;
        document.getElementById('client_message').setAttribute('style', 'display:block');
    }

    function initTable(){
        document.getElementById('client_message').setAttribute('style', 'display:none');
        LimitsUI.clearTableContent();
    }

    return {
        limitsHandler: limitsHandler,
        limitsError: limitsError,
        clean: initTable
    };
}());
<|endoftext|>


<commit_before>(function(){
	"use strict";
	/*
	  (TODO) data format for expedition table:
	  - for income modifier:

	      - standard modifier:

		  { type: "normal",
	        gs: true / false,
			daihatsu: 0 ~ 4
		  }

			  - "gs" indicates whether great success is intended
			  - whoever saves the data is responsible for its consistency
			    if say daihatsu value turns out to be 5, that's not my fault

		  - custom modifier:

          { type: "direct",
		    value: a number, from 1.0 to perhaps 2.0 (meaning 100% ~ 200%)
		  }

		      - great success should be taken into account by custom modifier.
			    which means if user intends to carry 4 daihatsus and ensure GS,
				this value needs to be 1.8 (1.5 * 1.2)
			  - the design decision is made in this way so that if future update
                adds some mechanism that affect GS modifier, we will still be flexible.

	  - for cost:
	      - cost deals with the problem that user might carry extra ships for
            a higher GS rate.

	      - standard:

		  { type: "costmodel",
		    wildcard: "DD" / "SS" / false,
			count: 0 ~ 6
		  }

		  - custom:

		  { type: "custom",
		    fuel: integer (non-negative),
			ammo: integer (non-negative)
		  }
	 */

	KC3StrategyTabs.expedtable = new KC3StrategyTab("expedtable");

	KC3StrategyTabs.expedtable.definition = {
		tabSelf: KC3StrategyTabs.expedtable,

		/* INIT: mandatory
		Prepares initial static data needed.
		---------------------------------*/
		init: function() {
		},

		/* RELOAD: optional
		Loads latest player or game data if needed.
		---------------------------------*/
		reload: function() {
		},

		/* EXECUTE: mandatory
		Places data onto the interface from scratch.
		---------------------------------*/
		execute: function() {
			var ExpedInfo = PS["KanColle.Expedition.New.Info"];
			var ExpedSType = PS["KanColle.Expedition.New.SType"];
			var factory = $(".tab_expedtable .factory");
			var expedTableRoot = $("#exped_table_content_root");
			var ExpedCostModel = PS["KanColle.Expedition.New.CostModel"];
			var Maybe = PS["Data.Maybe"];
			var PartialUnsafe = PS["Partial.Unsafe"];
			function calcCostModel(stypeInstance, num) {
				return ExpedCostModel.normalCostModel(stypeInstance)(num);
			}

			var allExpeds = [];
			var i;
			for (i=1; i<=40; ++i)
				allExpeds.push(i);

			function makeWinItem( jqObj, winItemArr ) {
				var itemId = winItemArr[0];
				var idToItem = {
					1: "bucket",
					2: "ibuild",
					3: "devmat",
					10: "box1",
					11: "box2",
					12: "box3"
				};
				if (itemId !== 0) {
					jqObj
						.append($(
							"<img>",
							{src: "../../assets/img/client/"+idToItem[itemId]+".png"}))
					    .append("x" + winItemArr[1]);
				} else {
					jqObj.text( "-" );
				}
			}

			allExpeds.forEach( function(eId) {
				var expedRow = $(".exped_row", factory).clone();
				var resourceInfo = ExpedInfo.getInformation( eId ).resource;
				var masterInfo = KC3Master._raw.mission[eId];

				$(".info_col.id", expedRow).text( eId );
				$(".info_col.time", expedRow).text( String( 60 * masterInfo.api_time ).toHHMMSS() );

				["fuel", "ammo", "steel", "bauxite"].forEach( function(name) {
					$(".info_col." + name, expedRow).text( resourceInfo[name] );
				});

				makeWinItem( $(".info_col.item1", expedRow), masterInfo.api_win_item1 );
				makeWinItem( $(".info_col.item2", expedRow), masterInfo.api_win_item2 );

				var mkFlg = () => Math.random() > 0.5;

				var flg = mkFlg();
				$(".modifier .view.view_general", expedRow).toggle( flg );
				$(".modifier .view.view_normal", expedRow).toggle( !flg );

				var flg2 = mkFlg();
				$(".cost .view.view_general", expedRow).toggle( flg2 );
				$(".cost .view.view_normal", expedRow).toggle( !flg2 );

				$(".modifier .view.view_general", expedRow).text("+50.00%");

				$(".modifier .view.view_normal img.gs", expedRow).attr(
					"src", mkFlg()
						? "../../assets/img/ui/btn-gs.png"
						: "../../assets/img/ui/btn-xgs.png" );
				$(".modifier .view.view_normal .dht_times", expedRow).text("x4");

				$(".cost .view.view_general .fuel", expedRow).text("-100");
				$(".cost .view.view_general .ammo", expedRow).text("-100");

				expedTableRoot.append( expedRow );
			});

			// setup cost model
			let contentRoot = $(".tab_expedtable #cost_model_content_root");
			let tableRoot = $("table", contentRoot);

			// setup slider controls
			let ticks = Array.from(Array(10 + 1).keys(), x => x * 10);
			let sliderSettings = {
				// ticks = [0,10..100]
				ticks: Array.from(Array(10 + 1).keys(), x => x * 10),
				step: 10,
				value: 80,
				tooltip: "hide"
			};
			let viewFuelPercent = $(".control_row.fuel .val");
			let viewAmmoPercent = $(".control_row.ammo .val");
			$("input#cost_model_fuel").slider(sliderSettings).on("change", function(e) {
				viewFuelPercent.text( e.value.newValue + "%" );
			});
			$("input#cost_model_ammo").slider(sliderSettings).on("change", function(e) {
				viewAmmoPercent.text( e.value.newValue + "%" );
			});

			// setup table
			let stypeTexts = [
				"DD", "CL", "CVLike", "SSLike",
				"CA", "BBV", "AS", "CT", "AV"];

			let tableBody = $("tbody",tableRoot);
			stypeTexts.map( function(stype) {
				let tblRow = $("<tr>");
				tblRow.append( $("<th>").text( stype ) );
				for (let i=1; i<=6; ++i) {

					let stypeInst = ExpedSType[stype].value;
					let costResult = calcCostModel(stypeInst, i);
					let cell;

					if (Maybe.isJust( costResult )) {
						cell = $(".tab_expedtable .factory .cost_cell").clone();
						let costSum = PartialUnsafe.unsafePartial(Maybe.fromJust)
							(costResult).reduce( function(acc, cur) {
							return { ammo: acc.ammo + cur.ammo,
									 fuel: acc.fuel + cur.fuel };
						}, {ammo: 0, fuel: 0});

						cell.data( "max-consumption", costSum );

						$(".ammo", cell).text( costSum.ammo );
						$(".fuel", cell).text( costSum.fuel );
					} else {
						cell = $(".tab_expedtable .factory .cost_cell_na").clone();
					}
					tblRow.append( $("<td />").append(cell) );
				}

				tableBody.append( tblRow );
			});
		},

		/* UPDATE: optional
		Partially update elements of the interface,
			possibly without clearing all contents first.
		Be careful! Do not only update new data,
			but also handle the old states (do cleanup).
		Return `false` if updating all needed,
			EXECUTE will be invoked instead.
		---------------------------------*/
		update: function(pageParams) {
			// Use `pageParams` for latest page hash values,
			// KC3StrategyTabs.pageParams keeps the old values for states tracking

			// Returning `true` means updating has been handled.
			return false;
		}
	};
})();
<commit_msg>cost update, cost model is done.<commit_after>(function(){
	"use strict";
	/*
	  (TODO) data format for expedition table:
	  - for income modifier:

	      - standard modifier:

		  { type: "normal",
	        gs: true / false,
			daihatsu: 0 ~ 4
		  }

			  - "gs" indicates whether great success is intended
			  - whoever saves the data is responsible for its consistency
			    if say daihatsu value turns out to be 5, that's not my fault

		  - custom modifier:

          { type: "direct",
		    value: a number, from 1.0 to perhaps 2.0 (meaning 100% ~ 200%)
		  }

		      - great success should be taken into account by custom modifier.
			    which means if user intends to carry 4 daihatsus and ensure GS,
				this value needs to be 1.8 (1.5 * 1.2)
			  - the design decision is made in this way so that if future update
                adds some mechanism that affect GS modifier, we will still be flexible.

	  - for cost:
	      - cost deals with the problem that user might carry extra ships for
            a higher GS rate.

	      - standard:

		  { type: "costmodel",
		    wildcard: "DD" / "SS" / false,
			count: 0 ~ 6
		  }

		  - custom:

		  { type: "custom",
		    fuel: integer (non-negative),
			ammo: integer (non-negative)
		  }
	 */

	KC3StrategyTabs.expedtable = new KC3StrategyTab("expedtable");

	KC3StrategyTabs.expedtable.definition = {
		tabSelf: KC3StrategyTabs.expedtable,

		/* INIT: mandatory
		Prepares initial static data needed.
		---------------------------------*/
		init: function() {
		},

		/* RELOAD: optional
		Loads latest player or game data if needed.
		---------------------------------*/
		reload: function() {
		},

		/* EXECUTE: mandatory
		Places data onto the interface from scratch.
		---------------------------------*/
		execute: function() {
			var ExpedInfo = PS["KanColle.Expedition.New.Info"];
			var ExpedSType = PS["KanColle.Expedition.New.SType"];
			var factory = $(".tab_expedtable .factory");
			var expedTableRoot = $("#exped_table_content_root");
			var ExpedCostModel = PS["KanColle.Expedition.New.CostModel"];
			var Maybe = PS["Data.Maybe"];
			var PartialUnsafe = PS["Partial.Unsafe"];
			function calcCostModel(stypeInstance, num) {
				return ExpedCostModel.normalCostModel(stypeInstance)(num);
			}

			var allExpeds = [];
			var i;
			for (i=1; i<=40; ++i)
				allExpeds.push(i);

			function makeWinItem( jqObj, winItemArr ) {
				var itemId = winItemArr[0];
				var idToItem = {
					1: "bucket",
					2: "ibuild",
					3: "devmat",
					10: "box1",
					11: "box2",
					12: "box3"
				};
				if (itemId !== 0) {
					jqObj
						.append($(
							"<img>",
							{src: "../../assets/img/client/"+idToItem[itemId]+".png"}))
					    .append("x" + winItemArr[1]);
				} else {
					jqObj.text( "-" );
				}
			}

			allExpeds.forEach( function(eId) {
				var expedRow = $(".exped_row", factory).clone();
				var resourceInfo = ExpedInfo.getInformation( eId ).resource;
				var masterInfo = KC3Master._raw.mission[eId];

				$(".info_col.id", expedRow).text( eId );
				$(".info_col.time", expedRow).text( String( 60 * masterInfo.api_time ).toHHMMSS() );

				["fuel", "ammo", "steel", "bauxite"].forEach( function(name) {
					$(".info_col." + name, expedRow).text( resourceInfo[name] );
				});

				makeWinItem( $(".info_col.item1", expedRow), masterInfo.api_win_item1 );
				makeWinItem( $(".info_col.item2", expedRow), masterInfo.api_win_item2 );

				var mkFlg = () => Math.random() > 0.5;

				var flg = mkFlg();
				$(".modifier .view.view_general", expedRow).toggle( flg );
				$(".modifier .view.view_normal", expedRow).toggle( !flg );

				var flg2 = mkFlg();
				$(".cost .view.view_general", expedRow).toggle( flg2 );
				$(".cost .view.view_normal", expedRow).toggle( !flg2 );

				$(".modifier .view.view_general", expedRow).text("+50.00%");

				$(".modifier .view.view_normal img.gs", expedRow).attr(
					"src", mkFlg()
						? "../../assets/img/ui/btn-gs.png"
						: "../../assets/img/ui/btn-xgs.png" );
				$(".modifier .view.view_normal .dht_times", expedRow).text("x4");

				$(".cost .view.view_general .fuel", expedRow).text("-100");
				$(".cost .view.view_general .ammo", expedRow).text("-100");

				expedTableRoot.append( expedRow );
			});

			// setup cost model
			let contentRoot = $(".tab_expedtable #cost_model_content_root");
			let tableRoot = $("table", contentRoot);

			// setup slider controls
			let ticks = Array.from(Array(10 + 1).keys(), x => x * 10);
			let sliderSettings = {
				// ticks = [0,10..100]
				ticks: Array.from(Array(10 + 1).keys(), x => x * 10),
				step: 10,
				// default of both fuel and ammo are 80%
				value: 80,
				tooltip: "hide"
			};

			let viewFuelPercent = $(".control_row.fuel .val");
			let viewAmmoPercent = $(".control_row.ammo .val");
			let tableBody = $("tbody",tableRoot);
			function updateCostModelTable( which, newValue ){
				console.assert( which === "fuel" || which === "ammo" );
				(  which === "fuel" ? viewFuelPercent
				 : which === "ammo" ? viewAmmoPercent
				 : undefined ).text( newValue + "%" );

				let actualPercent = (newValue + 0.0) / 100.0;
				$(".cost_cell", tableBody).each( function() {
					let jq = $(this);
					let maxCost = jq.data("max-cost");
					$("." + which, this).text( Math.floor(maxCost[which] * actualPercent) );
				});
			}

			$("input#cost_model_fuel").slider(sliderSettings).on("change", function(e) {
				updateCostModelTable( "fuel", e.value.newValue );
			});
			$("input#cost_model_ammo").slider(sliderSettings).on("change", function(e) {
				updateCostModelTable( "ammo", e.value.newValue );
			});

			// setup table
			let stypeTexts = [
				"DD", "CL", "CVLike", "SSLike",
				"CA", "BBV", "AS", "CT", "AV"];

			stypeTexts.map( function(stype) {
				let tblRow = $("<tr>");
				tblRow.append( $("<th>").text( stype ) );
				for (let i=1; i<=6; ++i) {

					let stypeInst = ExpedSType[stype].value;
					let costResult = calcCostModel(stypeInst, i);
					let cell;

					if (Maybe.isJust( costResult )) {
						cell = $(".tab_expedtable .factory .cost_cell").clone();
						let costSum = PartialUnsafe.unsafePartial(Maybe.fromJust)
							(costResult).reduce( function(acc, cur) {
							return { ammo: acc.ammo + cur.ammo,
									 fuel: acc.fuel + cur.fuel };
						}, {ammo: 0, fuel: 0});

						cell.data( "max-cost", costSum );
					} else {
						cell = $(".tab_expedtable .factory .cost_cell_na").clone();
					}
					tblRow.append( $("<td />").append(cell) );
				}

				tableBody.append( tblRow );
			});

			updateCostModelTable("fuel", 80);
			updateCostModelTable("ammo", 80);
		},

		/* UPDATE: optional
		Partially update elements of the interface,
			possibly without clearing all contents first.
		Be careful! Do not only update new data,
			but also handle the old states (do cleanup).
		Return `false` if updating all needed,
			EXECUTE will be invoked instead.
		---------------------------------*/
		update: function(pageParams) {
			// Use `pageParams` for latest page hash values,
			// KC3StrategyTabs.pageParams keeps the old values for states tracking

			// Returning `true` means updating has been handled.
			return false;
		}
	};
})();
<|endoftext|>


<commit_before>import React, { PropTypes, Component } from 'react';
import { QUESTION_STATS_COLORS } from '../../constants/Constants';
import Chart from 'chart.js';

export default class QuestionStatsGraph extends Component {
    static propTypes = {
        question: PropTypes.object.isRequired,
        userAnswer: PropTypes.object.isRequired
    };

    componentDidMount() {
        let question = this.props.question;
        let userAnswer = this.props.userAnswer;
        if (!question || !userAnswer) {
            return;
        }

        let options = {
            segmentShowStroke: false,
            percentageInnerCutout : 55
        };

        this.initQuestionStatsGraphs(question, userAnswer, options);
    }

    render() {
        let userAnswer = this.props.userAnswer;
        let question = this.props.question;
        if (!question) {
            return null;
        }

        return (

                    <canvas id={"female-answer-chart-" + userAnswer.answerId} width="150" height="150"></canvas>

Chicas

                    <canvas id={"male-answer-chart-" + userAnswer.answerId} width="150" height="150"></canvas>

Chicos

        );
    }

    getPercentage = function(count, totalCount) {
        return count && totalCount ? Math.round(count/totalCount*100) : 0;
    };

    initQuestionStatsGraphs(question, userAnswer, options) {
        let femaleStats = [];
        let maleStats = [];
        question.answers.forEach((answer, index) => {
            femaleStats.push({
                value: this.getPercentage(answer.femaleAnswersCount, question.femaleAnswersCount),
                color: QUESTION_STATS_COLORS[index]
            });
            maleStats.push({
                value: this.getPercentage(answer.maleAnswersCount, question.maleAnswersCount),
                color: QUESTION_STATS_COLORS[index]
            });
        });

        let ctx1 = document.getElementById("female-answer-chart-" + userAnswer.answerId).getContext("2d");
        let ctx2 = document.getElementById("male-answer-chart-" + userAnswer.answerId).getContext("2d");

        if (question.femaleAnswersCount) {
            new Chart(ctx1).Doughnut(femaleStats, options);
        }
        if (question.maleAnswersCount) {
            new Chart(ctx2).Doughnut(maleStats, options);
        }
    }
}
<commit_msg>QS-905: Add percentage symbol to graph tooltips<commit_after>import React, { PropTypes, Component } from 'react';
import { QUESTION_STATS_COLORS } from '../../constants/Constants';
import Chart from 'chart.js';

export default class QuestionStatsGraph extends Component {
    static propTypes = {
        question: PropTypes.object.isRequired,
        userAnswer: PropTypes.object.isRequired
    };

    componentDidMount() {
        let question = this.props.question;
        let userAnswer = this.props.userAnswer;
        if (!question || !userAnswer) {
            return;
        }

        let options = {
            segmentShowStroke: false,
            percentageInnerCutout : 55,
            tooltipTemplate: "<%= value %>%"
        };

        this.initQuestionStatsGraphs(question, userAnswer, options);
    }

    render() {
        let userAnswer = this.props.userAnswer;
        let question = this.props.question;
        if (!question) {
            return null;
        }

        return (

                    <canvas id={"female-answer-chart-" + userAnswer.answerId} width="150" height="150"></canvas>

Chicas

                    <canvas id={"male-answer-chart-" + userAnswer.answerId} width="150" height="150"></canvas>

Chicos

        );
    }

    getPercentage = function(count, totalCount) {
        return count && totalCount ? Math.round(count/totalCount*100) : 0;
    };

    initQuestionStatsGraphs(question, userAnswer, options) {
        let femaleStats = [];
        let maleStats = [];
        question.answers.forEach((answer, index) => {
            femaleStats.push({
                value: this.getPercentage(answer.femaleAnswersCount, question.femaleAnswersCount),
                color: QUESTION_STATS_COLORS[index]
            });
            maleStats.push({
                value: this.getPercentage(answer.maleAnswersCount, question.maleAnswersCount),
                color: QUESTION_STATS_COLORS[index]
            });
        });

        let ctx1 = document.getElementById("female-answer-chart-" + userAnswer.answerId).getContext("2d");
        let ctx2 = document.getElementById("male-answer-chart-" + userAnswer.answerId).getContext("2d");

        if (question.femaleAnswersCount) {
            new Chart(ctx1).Doughnut(femaleStats, options);
        }
        if (question.maleAnswersCount) {
            new Chart(ctx2).Doughnut(maleStats, options);
        }
    }
}
<|endoftext|>