<commit_before>'use strict';

/* Services */

var visitServices = angular.module('visitServices', ['ngResource']);
var songServices = angular.module('songServices', ['ngResource']);

songServices.factory('Song', ['$resource',
    function($resource) {
        return $resource('/voornaaminliedje/api/admin/songs/:id', {
            id: '@id'
        }, {
            query: {
                method: 'GET',
                params: {
                    page: '',
                    count: '',
                    sortingArtist: '',
                    sortingTitle: '',
                    filterArtist: '',
                    filterTitle: ''
                },
                isArray: true
            },
            get: {
                method: 'GET',
                params: {
                    id: ''
                },
                isArray: false
            },
            /*save: {
                method: 'POST',
                params: {
                    title: ''
                }
            },*/
            update: {
                method: 'PUT',
                params: {
                    artist: '',
                    title: ''
                }
            }
        });
    }
]);

visitServices.factory('Visit', ['Base64', '$http', '$resource',
    function(Base64, $http, $resource) {

        return $resource('/voornaaminliedje/api/admin/visit/all', {}, {
            query: {
                method: 'GET',
                params: {
                    visitId: 'visits'
                },
                isArray: true
            }
        });
    }
]);

visitServices.factory('VisitDetail', ['Base64', '$http', '$resource',
    function(Base64, $http, $resource) {
        return $resource('/voornaaminliedje/api/admin/visit/:id', {}, {
            get: {
                method: 'GET',
                params: {
                    id: ''
                }
            }
        });
    }
]);

visitServices.factory('SongDetail', ['Base64', '$http', '$resource',
    function(Base64, $http, $resource) {
        return $resource('/voornaaminliedje/api/admin/songs/:id', {}, {
            get: {
                method: 'GET',
                params: {
                    id: ''
                }
            }
        });
    }
]);

visitServices.factory('Auth', ['Base64', '$cookieStore', '$http', '$location',
    function(Base64, $cookieStore, $http, $location) {
        // initialize to whatever is in the cookie, if anything
        $http.defaults.headers.common['Authorization'] = 'Basic ' + $cookieStore.get('authdata');

        return {
            setCredentials: function(username, password) {
                var encoded = Base64.encode(username + ':' + password);
                console.log("Settings credentials...");
                $http.defaults.headers.common.Authorization = 'Basic ' + encoded;
                $cookieStore.put('authdata', encoded);
                $cookieStore.put('user', username);
                $location.path('/visits');
            },
            clearCredentials: function() {
                document.execCommand("ClearAuthenticationCache");
                console.log("Clearing credentials...");
                $cookieStore.remove('authdata');
                $cookieStore.remove('user');
                $http.defaults.headers.common.Authorization = 'Basic ';
            }
        };
    }
]);

visitServices.factory('Base64', function() {
    var keyStr = 'ABCDEFGHIJKLMNOP' +
        'QRSTUVWXYZabcdef' +
        'ghijklmnopqrstuv' +
        'wxyz0123456789+/' +
        '=';
    return {
        encode: function(input) {
            var output = "";
            var chr1, chr2, chr3 = "";
            var enc1, enc2, enc3, enc4 = "";
            var i = 0;

            do {
                chr1 = input.charCodeAt(i++);
                chr2 = input.charCodeAt(i++);
                chr3 = input.charCodeAt(i++);

                enc1 = chr1 >> 2;
                enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
                enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
                enc4 = chr3 & 63;

                if (isNaN(chr2)) {
                    enc3 = enc4 = 64;
                } else if (isNaN(chr3)) {
                    enc4 = 64;
                }

                output = output +
                    keyStr.charAt(enc1) +
                    keyStr.charAt(enc2) +
                    keyStr.charAt(enc3) +
                    keyStr.charAt(enc4);
                chr1 = chr2 = chr3 = "";
                enc1 = enc2 = enc3 = enc4 = "";
            } while (i < input.length);

            return output;
        },

        decode: function(input) {
            var output = "";
            var chr1, chr2, chr3 = "";
            var enc1, enc2, enc3, enc4 = "";
            var i = 0;

            // remove all characters that are not A-Z, a-z, 0-9, +, /, or =
            var base64test = /[^A-Za-z0-9\+\/\=]/g;
            if (base64test.exec(input)) {
                alert("There were invalid base64 characters in the input text.\n" +
                    "Valid base64 characters are A-Z, a-z, 0-9, '+', '/',and '='\n" +
                    "Expect errors in decoding.");
            }
            input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");

            do {
                enc1 = keyStr.indexOf(input.charAt(i++));
                enc2 = keyStr.indexOf(input.charAt(i++));
                enc3 = keyStr.indexOf(input.charAt(i++));
                enc4 = keyStr.indexOf(input.charAt(i++));

                chr1 = (enc1 << 2) | (enc2 >> 4);
                chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
                chr3 = ((enc3 & 3) << 6) | enc4;

                output = output + String.fromCharCode(chr1);

                if (enc3 != 64) {
                    output = output + String.fromCharCode(chr2);
                }
                if (enc4 != 64) {
                    output = output + String.fromCharCode(chr3);
                }

                chr1 = chr2 = chr3 = "";
                enc1 = enc2 = enc3 = enc4 = "";

            } while (i < input.length);

            return output;
        }
    };
});

visitServices.factory('ErrorService', function() {
    return {
        errorMessage: null,
        setError: function(msg) {
            console.log("Setting error " + msg);
            this.errorMessage = msg;
        },
        clear: function() {
            this.errorMessage = null;
        }
    };
});

// register the interceptor as a service
visitServices.factory('errorHttpInterceptor', function($q, $location, $cookieStore, ErrorService) {
    return {
        // optional method
        'request': function(config) {
            // do something on success
            return config || $q.when(config);
        },

        // optional method
        'requestError': function(rejection) {
            // do something on error
            console.log("Request error is " + rejection.status);
            return $q.reject(rejection);
        },

        // optional method
        'response': function(response) {
            // do something on success
            return response || $q.when(response);
        },

        // optional method
        'responseError': function(rejection) {
            // do something on error
            console.log("Response error is " + rejection.status);
            ErrorService.setError('Fout is ' + rejection.status);
            console.log("Response error is set? " + ErrorService.errorMessage);
            console.log("Cookie aanwezig? " + $cookieStore.get('authdata'));
            if (rejection.status == 401) {
                $location.path('/login');
            } else {
                $location.path('/error')
            }
            return $q.reject(rejection);
        }
    };
});
<commit_msg>Added check for cookie for better errorMessage.<commit_after>'use strict';

/* Services */

var visitServices = angular.module('visitServices', ['ngResource']);
var songServices = angular.module('songServices', ['ngResource']);

songServices.factory('Song', ['$resource',
    function($resource) {
        return $resource('/voornaaminliedje/api/admin/songs/:id', {
            id: '@id'
        }, {
            query: {
                method: 'GET',
                params: {
                    page: '',
                    count: '',
                    sortingArtist: '',
                    sortingTitle: '',
                    filterArtist: '',
                    filterTitle: ''
                },
                isArray: true
            },
            get: {
                method: 'GET',
                params: {
                    id: ''
                },
                isArray: false
            },
            /*save: {
                method: 'POST',
                params: {
                    title: ''
                }
            },*/
            update: {
                method: 'PUT',
                params: {
                    artist: '',
                    title: ''
                }
            }
        });
    }
]);

visitServices.factory('Visit', ['Base64', '$http', '$resource',
    function(Base64, $http, $resource) {

        return $resource('/voornaaminliedje/api/admin/visit/all', {}, {
            query: {
                method: 'GET',
                params: {
                    visitId: 'visits'
                },
                isArray: true
            }
        });
    }
]);

visitServices.factory('VisitDetail', ['Base64', '$http', '$resource',
    function(Base64, $http, $resource) {
        return $resource('/voornaaminliedje/api/admin/visit/:id', {}, {
            get: {
                method: 'GET',
                params: {
                    id: ''
                }
            }
        });
    }
]);

visitServices.factory('SongDetail', ['Base64', '$http', '$resource',
    function(Base64, $http, $resource) {
        return $resource('/voornaaminliedje/api/admin/songs/:id', {}, {
            get: {
                method: 'GET',
                params: {
                    id: ''
                }
            }
        });
    }
]);

visitServices.factory('Auth', ['Base64', '$cookieStore', '$http', '$location',
    function(Base64, $cookieStore, $http, $location) {
        // initialize to whatever is in the cookie, if anything
        $http.defaults.headers.common['Authorization'] = 'Basic ' + $cookieStore.get('authdata');

        return {
            setCredentials: function(username, password) {
                var encoded = Base64.encode(username + ':' + password);
                console.log("Settings credentials...");
                $http.defaults.headers.common.Authorization = 'Basic ' + encoded;
                $cookieStore.put('authdata', encoded);
                $cookieStore.put('user', username);
                $location.path('/visits');
            },
            clearCredentials: function() {
                document.execCommand("ClearAuthenticationCache");
                console.log("Clearing credentials...");
                $cookieStore.remove('authdata');
                $cookieStore.remove('user');
                $http.defaults.headers.common.Authorization = 'Basic ';
            }
        };
    }
]);

visitServices.factory('Base64', function() {
    var keyStr = 'ABCDEFGHIJKLMNOP' +
        'QRSTUVWXYZabcdef' +
        'ghijklmnopqrstuv' +
        'wxyz0123456789+/' +
        '=';
    return {
        encode: function(input) {
            var output = "";
            var chr1, chr2, chr3 = "";
            var enc1, enc2, enc3, enc4 = "";
            var i = 0;

            do {
                chr1 = input.charCodeAt(i++);
                chr2 = input.charCodeAt(i++);
                chr3 = input.charCodeAt(i++);

                enc1 = chr1 >> 2;
                enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
                enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
                enc4 = chr3 & 63;

                if (isNaN(chr2)) {
                    enc3 = enc4 = 64;
                } else if (isNaN(chr3)) {
                    enc4 = 64;
                }

                output = output +
                    keyStr.charAt(enc1) +
                    keyStr.charAt(enc2) +
                    keyStr.charAt(enc3) +
                    keyStr.charAt(enc4);
                chr1 = chr2 = chr3 = "";
                enc1 = enc2 = enc3 = enc4 = "";
            } while (i < input.length);

            return output;
        },

        decode: function(input) {
            var output = "";
            var chr1, chr2, chr3 = "";
            var enc1, enc2, enc3, enc4 = "";
            var i = 0;

            // remove all characters that are not A-Z, a-z, 0-9, +, /, or =
            var base64test = /[^A-Za-z0-9\+\/\=]/g;
            if (base64test.exec(input)) {
                alert("There were invalid base64 characters in the input text.\n" +
                    "Valid base64 characters are A-Z, a-z, 0-9, '+', '/',and '='\n" +
                    "Expect errors in decoding.");
            }
            input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");

            do {
                enc1 = keyStr.indexOf(input.charAt(i++));
                enc2 = keyStr.indexOf(input.charAt(i++));
                enc3 = keyStr.indexOf(input.charAt(i++));
                enc4 = keyStr.indexOf(input.charAt(i++));

                chr1 = (enc1 << 2) | (enc2 >> 4);
                chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
                chr3 = ((enc3 & 3) << 6) | enc4;

                output = output + String.fromCharCode(chr1);

                if (enc3 != 64) {
                    output = output + String.fromCharCode(chr2);
                }
                if (enc4 != 64) {
                    output = output + String.fromCharCode(chr3);
                }

                chr1 = chr2 = chr3 = "";
                enc1 = enc2 = enc3 = enc4 = "";

            } while (i < input.length);

            return output;
        }
    };
});

visitServices.factory('ErrorService', function() {
    return {
        errorMessage: null,
        setError: function(msg) {
            console.log("Setting error " + msg);
            this.errorMessage = msg;
        },
        clear: function() {
            this.errorMessage = null;
        }
    };
});

// register the interceptor as a service
visitServices.factory('errorHttpInterceptor', function($q, $location, $cookieStore, ErrorService) {
    return {
        // optional method
        'request': function(config) {
            // do something on success
            return config || $q.when(config);
        },

        // optional method
        'requestError': function(rejection) {
            // do something on error
            console.log("Request error is " + rejection.status);
            return $q.reject(rejection);
        },

        // optional method
        'response': function(response) {
            // do something on success
            return response || $q.when(response);
        },

        // optional method
        'responseError': function(rejection) {
            // do something on error
            console.log("Response error is " + rejection.status);
            console.log("Cookie aanwezig? " + $cookieStore.get('authdata'));
            if (rejection.status == 401) {
                if ($cookieStore.get('authdata')) {
                    ErrorService.setError('Gebruikersnaam en/of wachtwoord onjuist');
                } else {
                    ErrorService.setError('Je moet ingelogd zijn om de pagina te raadplegen');
                }
                $location.path('/login');
            } else {
                ErrorService.setError('Meld admin@voornaaminliedje de volgende fout: ' + rejection.status);
                $location.path('/error')
            }
            console.log("Response error is set? " + ErrorService.errorMessage);
            return $q.reject(rejection);
        }
    };
});
<|endoftext|>


<commit_before>/**
 * author: Nils Gehlenborg - nils@hms.harvard.edu
*/

function Selection( items ) {
    this.items = items || [];
}

// should be a singleton
function SelectionList( colors ) {
    this.list = [];
    this.colorMap = {};
    this.colors = colors || d3.scale.category10().range();

    this.addSelection = function( selection ) {
        this.colorMap[this.list.length] = this._nextColor();
        console.log( this.colorMap );
        this.list.push( selection );

        $(EventManager).trigger( "item-selection-added", { selection: selection } );

        return this;
    };

    this.removeSelection = function( selection ) {
        for ( var i = 0; i < this.list.length; ++i ) {
            if ( this.list[i] === selection ) {
                console.log( 'Deleting selection ' + i + '.' );

                // remove selection from list
                this.list.splice(i,1);

                // remove selection from color map
                delete this.colorMap[getSelectionIndex(selection)];
            }
        }

        console.log( 'Unable to delete selection.' );
    };

    this.getSelectionIndex = function(selection){
        for ( var i = 0; i < this.list.length; ++i ) {
            if ( this.list[i] === selection ) {
                return i;
            }
        }

        return undefined;
    }

    this.getSelection = function(index) {
        try {
            return ( this.list[index] );
        }
        catch ( error ) {
            // ignore
        }

        return undefined;
    };

    this.getColor = function( selection ) {
        try {
            return ( this.colorMap[this.getSelectionIndex(selection)] );
        }
        catch ( error ) {
            // ignore
        }

        return undefined;
    }

    this.getSize = function() {
        return this.list.length;
    }

    this._nextColor = function() {
        /*
        // assign colors using round-robin approach
        // minimizes re-use of colors
        var color = this.colors.splice(0,1)[0];
        this.color.push(color);
        return ( color );
        */

        // assign colors based on selection creation order
        // minimizes chance to have same color assigned to two or more selections
        return this.colors[this.list.length];
    }
}
<commit_msg>Now generating UUIDs for selection. Changed color mapping from first-in-list to color pool.<commit_after>/**
 * author: Nils Gehlenborg - nils@hms.harvard.edu
*/

function Selection( items ) {
    this.items = items || [];
    this.id = undefined;
}

// should be a singleton
function SelectionList( palette ) {
    this.list = [];
    this.colors = {};
    this.palette = palette || d3.scale.category10().range();

    this.addSelection = function( selection ) {
        selection.id = this._nextId();
        this.list.push( selection );

        this.colors[selection.id] = this._nextColor();

        $(EventManager).trigger( "item-selection-added", { selection: selection } );

        return this;
    };

    this.removeSelection = function( selection ) {
        for ( var i = 0; i < this.list.length; ++i ) {
            if ( this.list[i] === selection ) {
                console.log( 'Deleting selection ' + i + '.' );

                // remove selection from list
                this.list.splice(i,1);

                // return color to palette
                this.palette.push(this.colors[selection.id]);

                // remove selection from color map
                delete this.colors[selection.id];

                // clear selection id
                selection.id = undefined;

                $(EventManager).trigger( "item-selection-removed", { selection: selection, index: i } );

                return;
            }
        }

        console.log( 'Unable to delete selection.' );
    };

    this.getSelectionIndex = function(selection){
        for ( var i = 0; i < this.list.length; ++i ) {
            if ( this.list[i] === selection ) {
                return i;
            }
        }

        return undefined;
    }

    this.getSelection = function(index) {
        try {
            return ( this.list[index] );
        }
        catch ( error ) {
            // ignore
        }

        return undefined;
    };

    this.getColor = function( selection ) {
        try {
            return ( this.colors[selection.id] );
        }
        catch ( error ) {
            // ignore
        }

        return undefined;
    }

    this.getSize = function() {
        return this.list.length;
    }

    this._nextColor = function() {
        // use color pool and return black once pool is empty
        if ( this.palette.length > 0 ) {
            // first available color
            return this.palette.splice(0,1)[0];
        }

        return "#000";
    }

    this._nextId = function() {
        // see broofa's answer in http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript
        return ( 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
            var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
            return v.toString(16) ;
        }) );
    }
}
<|endoftext|>


<commit_before>var fs = require('fs');
var spawn = require('child_process').spawn;

// simple args parsing
var rebuild = false, buildThreadsCount = 1;
for (var i = 2; i < process.argv.length;) {
  var cmd = process.argv[i++];
  switch (cmd) {
    case '--threads':
    case '-t':
      buildThreadsCount = Math.max(1, process.argv[i++] | 0);
      break;
    case '--rebuild':
    case '-r':
      rebuild = true;
      break;
    default:
      throw new Error('Bad argument: ' + cmd);
  }
}

var build_dir = '../build/playerglobal';

var manifest = JSON.parse(fs.readFileSync('manifest.json'));
var manifestUpdated = fs.statSync('manifest.json').mtime.valueOf();
var jstemplate = '' + fs.readFileSync('playerglobal.js.template');
var ascjar = '../utils/asc.jar';
var buildasc = './avm2/generated/builtin/builtin.abc';

// switching working dir to ./src
process.chdir('../../src');

// prepare build folder
if (!fs.existsSync(build_dir)) {
  fs.mkdirSync(build_dir);
}
var dependencies = {files: {}, abcs: {}}, dependenciesUpdated = 0;
var dependenciesPath = build_dir + '/dependencies.json';
if (fs.existsSync(dependenciesPath) && !rebuild) {
  dependenciesUpdated = fs.statSync(dependenciesPath).mtime.valueOf();
  // discarding previous build if manifest was updated
  if (manifestUpdated <= dependenciesUpdated) {
    dependencies = JSON.parse(fs.readFileSync(dependenciesPath));
  } else {
    console.info('New manifest was found: refreshing builds');
  }
}

// reset file dependencies
Object.keys(dependencies.files).forEach(function (file) {
  var isNew = true;
  try {
    fileUpdated = fs.statSync(file).mtime.valueOf();
    isNew = fileUpdated > dependenciesUpdated;
  } catch(e) {}
  if (isNew) {
    dependencies.files[file].forEach(function (abc) {
      delete dependencies.abcs[abc];
    });
    delete dependencies.files[file];
  }
});

var buildQueue = [];
manifest.forEach(function (item) {
  var outputPath = build_dir + '/' + item.name + '.abc';
  item.outputPath = outputPath;
  // rebuilding if abc does not exist or removed from dependencies
  if (!fs.existsSync(outputPath) || !(item.name in dependencies.abcs)) {
    buildQueue.push(item);
  } else {
    item.dependencies = dependencies.abcs[item.name];
  }
});

function ensureDir(dir) {
  if (fs.existsSync(dir)) return;
  var parts = dir.split('/'), i = parts.length;
  while (!fs.existsSync(parts.slice(0, i - 1).join('/'))) {
    i--;
    if (i <= 0) throw new Error();
  }

  while (i <= parts.length) {
    fs.mkdirSync(parts.slice(0, i).join('/'));
    i++;
  }
}

function runAsc(threadId, outputPath, files, callback) {
  console.info('Building ' + outputPath + ' [' + threadId + '] ...');
  var outputDir = outputPath.substring(0, outputPath.lastIndexOf('/'));
  ensureDir(outputDir);
  var outputName = outputPath.substring(outputDir.length + 1);
  outputName = outputName.substring(0, outputName.lastIndexOf('.'));
  if (fs.existsSync(outputPath)) {
    fs.unlink(outputPath);
  }

  var args = ['-ea', '-DAS3', '-DAVMPLUS', '-classpath', ascjar,
              'macromedia.asc.embedding.ScriptCompiler', '-builtin',
              '-import', buildasc, '-outdir', outputDir,
              '-out', outputName].concat(files);
  var proc = spawn('java', args, {stdio: 'inherit'} );
  proc.on('close', function (code) {
    if (!fs.existsSync(outputPath)) {
      code = -1;
    }

    callback(code, outputPath);
  });
}

function getDependencies(files) {
  var tmp = {}, tmpErr = {}, queue = [];
  files.forEach(function (file) {
    tmp[file] = true;
    queue.push(file);
  });
  while (queue.length > 0) {
    var file = queue.pop();
    try {
      var content = '' + fs.readFileSync(file);
      var m, re = /\bimport\s+(flash[\w\.]+)\s*;/g;
      while ((m = re.exec(content))) {
        var path = m[1].replace(/\./g, '/') + '.as';
        if (fs.existsSync(path)) {
          if (!tmp[path]) {
            tmp[path] = true;
            // uncomment to do recursive search of dependencies
            // queue.push(path);
          }
        } else if (!tmpErr[m[0]]) {
          console.log('Dependency file for \"' + m[0] + '\" was not found');
          tmpErr[m[0]] = true;
        }
      }
    } catch (e) {
      console.log('Unable to get dependencies from ' + file + ': ' + e);
    }
  }
  return Object.keys(tmp);
}

var buildError = false, pending = 0;
function buildNext(threadId) {
  pending++;
  var item = buildQueue.shift();
  runAsc(threadId, item.outputPath, item.files, function (code, output) {
    if (buildError) {
      return; // ignoring parallel builds if error happend
    }
    if (code) {
      buildError = true;
      throw new Error('Build returned error code: ' + code);
    }

    item.dependencies = getDependencies(item.files);

    pending--;
    if (buildQueue.length === 0) {
      if (pending === 0) {
        completeBuild();
      }
      return;
    }

    buildNext(threadId);
  });
}

var updateDependencies = false;
if (buildQueue.length > 0) {
  updateDependencies = true;
  for (var i = 0; i < buildThreadsCount && buildQueue.length > 0; i++) {
    buildNext(i + 1);
  }
} else {
  completeBuild();
}

function updatePlayerglobal() {
  console.info('Updating playerglobal.js');

  var hex = '', lastPos = 0;
  manifest.forEach(function (item) {
    var file = item.outputPath;
    var ascData = fs.readFileSync(item.outputPath);
    delete item.outputPath;
    var ascHex = ascData.toString('hex');
    item.offset = lastPos;
    item.length = ascHex.length >> 1;
    lastPos += item.length;
    hex += ascHex;
  });
  fs.writeFileSync('flash/playerglobal-new.js',
    jstemplate.replace('[/*index*/]', JSON.stringify(manifest, null, 2)));
  fs.writeFileSync('flash/playerglobal-new.abc', new Buffer(hex, 'hex'));
}

function completeBuild() {
  var needsNewPlayerglobal = true;
  if (updateDependencies) {
    console.info('Updating dependencies');
    dependencies.files = {};
    dependencies.abcs = {};
    manifest.forEach(function (item) {
      var files = item.dependencies, name = item.name;
      files.forEach(function (file) {
        var abcs = dependencies.files[file];
        if (!abcs) {
          dependencies.files[file] = [name];
        } else {
          abcs.push(name);
        }
      });
      dependencies.abcs[name] = files;
      delete item.dependencies;
      delete item.files;
    });

    fs.writeFileSync(dependenciesPath, JSON.stringify(dependencies, null, 2));
  } else {
    console.info('Checking playerglobal.js');
    try {
      var playerglobalUpdated = fs.statSync('flash/playerglobal-new.js').mtime.valueOf();
      needsNewPlayerglobal = dependenciesUpdated > playerglobalUpdated;
    } catch (e) {}
  }

  if (needsNewPlayerglobal) {
    updatePlayerglobal();
  }
  console.log('Done.');
}
<commit_msg>Adds base classes to the dependencies<commit_after>/* -*- Mode: js; js-indent-level: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/*
 * Copyright 2014 Mozilla Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var fs = require('fs');
var spawn = require('child_process').spawn;

// simple args parsing
var rebuild = false, buildThreadsCount = 1;
for (var i = 2; i < process.argv.length;) {
  var cmd = process.argv[i++];
  switch (cmd) {
    case '--threads':
    case '-t':
      buildThreadsCount = Math.max(1, process.argv[i++] | 0);
      break;
    case '--rebuild':
    case '-r':
      rebuild = true;
      break;
    default:
      throw new Error('Bad argument: ' + cmd);
  }
}

var build_dir = '../build/playerglobal';

var manifest = JSON.parse(fs.readFileSync('manifest.json'));
var manifestUpdated = fs.statSync('manifest.json').mtime.valueOf();
var jstemplate = '' + fs.readFileSync('playerglobal.js.template');
var ascjar = '../utils/asc.jar';
var buildasc = './avm2/generated/builtin/builtin.abc';

// switching working dir to ./src
process.chdir('../../src');

// prepare build folder
if (!fs.existsSync(build_dir)) {
  fs.mkdirSync(build_dir);
}
var dependencies = {files: {}, abcs: {}}, dependenciesUpdated = 0;
var dependenciesPath = build_dir + '/dependencies.json';
if (fs.existsSync(dependenciesPath) && !rebuild) {
  dependenciesUpdated = fs.statSync(dependenciesPath).mtime.valueOf();
  // discarding previous build if manifest was updated
  if (manifestUpdated <= dependenciesUpdated) {
    dependencies = JSON.parse(fs.readFileSync(dependenciesPath));
  } else {
    console.info('New manifest was found: refreshing builds');
  }
}

// reset file dependencies
Object.keys(dependencies.files).forEach(function (file) {
  var isNew = true;
  try {
    fileUpdated = fs.statSync(file).mtime.valueOf();
    isNew = fileUpdated > dependenciesUpdated;
  } catch(e) {}
  if (isNew) {
    dependencies.files[file].forEach(function (abc) {
      delete dependencies.abcs[abc];
    });
    delete dependencies.files[file];
  }
});

var buildQueue = [];
manifest.forEach(function (item) {
  var outputPath = build_dir + '/' + item.name + '.abc';
  item.outputPath = outputPath;
  // rebuilding if abc does not exist or removed from dependencies
  if (!fs.existsSync(outputPath) || !(item.name in dependencies.abcs)) {
    buildQueue.push(item);
  } else {
    item.dependencies = dependencies.abcs[item.name];
  }
});

function ensureDir(dir) {
  if (fs.existsSync(dir)) return;
  var parts = dir.split('/'), i = parts.length;
  while (!fs.existsSync(parts.slice(0, i - 1).join('/'))) {
    i--;
    if (i <= 0) throw new Error();
  }

  while (i <= parts.length) {
    fs.mkdirSync(parts.slice(0, i).join('/'));
    i++;
  }
}

function runAsc(threadId, outputPath, files, callback) {
  console.info('Building ' + outputPath + ' [' + threadId + '] ...');
  var outputDir = outputPath.substring(0, outputPath.lastIndexOf('/'));
  ensureDir(outputDir);
  var outputName = outputPath.substring(outputDir.length + 1);
  outputName = outputName.substring(0, outputName.lastIndexOf('.'));
  if (fs.existsSync(outputPath)) {
    fs.unlink(outputPath);
  }

  var args = ['-ea', '-DAS3', '-DAVMPLUS', '-classpath', ascjar,
              'macromedia.asc.embedding.ScriptCompiler', '-builtin',
              '-import', buildasc, '-outdir', outputDir,
              '-out', outputName].concat(files);
  var proc = spawn('java', args, {stdio: 'inherit'} );
  proc.on('close', function (code) {
    if (!fs.existsSync(outputPath)) {
      code = -1;
    }

    callback(code, outputPath);
  });
}

function getDependencies(files) {
  var tmp = {}, tmpErr = {}, queue = [];
  files.forEach(function (file) {
    tmp[file] = true;
    queue.push(file);
  });
  while (queue.length > 0) {
    var file = queue.pop();
    try {
      var content = '' + fs.readFileSync(file);
      var m, re = /\bimport\s+(flash[\w\.]+)\s*;/g;
      while ((m = re.exec(content))) {
        var path = m[1].replace(/\./g, '/') + '.as';
        if (fs.existsSync(path)) {
          if (!tmp[path]) {
            tmp[path] = true;
            // uncomment to do recursive search of dependencies
            // queue.push(path);
          }
        } else if (!tmpErr[m[0]]) {
          console.log('Dependency file for \"' + m[0] + '\" was not found');
          tmpErr[m[0]] = true;
        }
      }
      m = /\bclass\s+\w+([^{]+)/.exec(content);
      if (m) {
        var baseClasses = m[1].replace(/\b(extends|implements)\b/g, '');
        re = /(\w+)/g;
        while ((m = re.exec(baseClasses))) {
          var path = file.substring(0, file.lastIndexOf('/') + 1) + m[1] + '.as';
          if (fs.existsSync(path)) {
            tmp[path] = true;
            // uncomment to do recursive search of dependencies
            // queue.push(path);
          }
        }
      }
    } catch (e) {
      console.log('Unable to get dependencies from ' + file + ': ' + e);
    }
  }
  return Object.keys(tmp);
}

var buildError = false, pending = 0;
function buildNext(threadId) {
  pending++;
  var item = buildQueue.shift();
  runAsc(threadId, item.outputPath, item.files, function (code, output) {
    if (buildError) {
      return; // ignoring parallel builds if error happend
    }
    if (code) {
      buildError = true;
      throw new Error('Build returned error code: ' + code);
    }

    item.dependencies = getDependencies(item.files);

    pending--;
    if (buildQueue.length === 0) {
      if (pending === 0) {
        completeBuild();
      }
      return;
    }

    buildNext(threadId);
  });
}

var updateDependencies = false;
if (buildQueue.length > 0) {
  updateDependencies = true;
  for (var i = 0; i < buildThreadsCount && buildQueue.length > 0; i++) {
    buildNext(i + 1);
  }
} else {
  completeBuild();
}

function updatePlayerglobal() {
  console.info('Updating playerglobal.js');

  var hex = '', lastPos = 0;
  manifest.forEach(function (item) {
    var file = item.outputPath;
    var ascData = fs.readFileSync(item.outputPath);
    delete item.outputPath;
    var ascHex = ascData.toString('hex');
    item.offset = lastPos;
    item.length = ascHex.length >> 1;
    lastPos += item.length;
    hex += ascHex;
  });
  fs.writeFileSync('flash/playerglobal-new.js',
    jstemplate.replace('[/*index*/]', JSON.stringify(manifest, null, 2)));
  fs.writeFileSync('flash/playerglobal-new.abc', new Buffer(hex, 'hex'));
}

function completeBuild() {
  var needsNewPlayerglobal = true;
  if (updateDependencies) {
    console.info('Updating dependencies');
    dependencies.files = {};
    dependencies.abcs = {};
    manifest.forEach(function (item) {
      var files = item.dependencies, name = item.name;
      files.forEach(function (file) {
        var abcs = dependencies.files[file];
        if (!abcs) {
          dependencies.files[file] = [name];
        } else {
          abcs.push(name);
        }
      });
      dependencies.abcs[name] = files;
      delete item.dependencies;
      delete item.files;
    });

    fs.writeFileSync(dependenciesPath, JSON.stringify(dependencies, null, 2));
  } else {
    console.info('Checking playerglobal.js');
    try {
      var playerglobalUpdated = fs.statSync('flash/playerglobal-new.js').mtime.valueOf();
      needsNewPlayerglobal = dependenciesUpdated > playerglobalUpdated;
    } catch (e) {}
  }

  if (needsNewPlayerglobal) {
    updatePlayerglobal();
  }
  console.log('Done.');
}
<|endoftext|>


<commit_before>/**
 * @module zrender/graphic/Style
 */
define(function (require) {

    var textHelper = require('./helper/text');

    var STYLE_COMMON_PROPS = [
        ['shadowBlur', 0], ['shadowOffsetX', 0], ['shadowOffsetY', 0], ['shadowColor', '#000'],
        ['lineCap', 'butt'], ['lineJoin', 'miter'], ['miterLimit', 10]
    ];

    // var SHADOW_PROPS = STYLE_COMMON_PROPS.slice(0, 4);
    // var LINE_PROPS = STYLE_COMMON_PROPS.slice(4);

    var Style = function (opts, host) {
        this.extendFrom(opts, false);
        this.host = host;
    };

    function createLinearGradient(ctx, obj, rect) {
        var x = obj.x == null ? 0 : obj.x;
        var x2 = obj.x2 == null ? 1 : obj.x2;
        var y = obj.y == null ? 0 : obj.y;
        var y2 = obj.y2 == null ? 0 : obj.y2;

        if (!obj.global) {
            x = x * rect.width + rect.x;
            x2 = x2 * rect.width + rect.x;
            y = y * rect.height + rect.y;
            y2 = y2 * rect.height + rect.y;
        }

        var canvasGradient = ctx.createLinearGradient(x, y, x2, y2);

        return canvasGradient;
    }

    function createRadialGradient(ctx, obj, rect) {
        var width = rect.width;
        var height = rect.height;
        var min = Math.min(width, height);

        var x = obj.x == null ? 0.5 : obj.x;
        var y = obj.y == null ? 0.5 : obj.y;
        var r = obj.r == null ? 0.5 : obj.r;
        if (!obj.global) {
            x = x * width + rect.x;
            y = y * height + rect.y;
            r = r * min;
        }

        var canvasGradient = ctx.createRadialGradient(x, y, 0, x, y, r);

        return canvasGradient;
    }

    Style.prototype = {

        constructor: Style,

        /**
         * @type {module:zrender/graphic/Displayable}
         */
        host: null,

        /**
         * @type {string}
         */
        fill: '#000000',

        /**
         * @type {string}
         */
        stroke: null,

        /**
         * @type {number}
         */
        opacity: 1,

        /**
         * @type {Array.<number>}
         */
        lineDash: null,

        /**
         * @type {number}
         */
        lineDashOffset: 0,

        /**
         * @type {number}
         */
        shadowBlur: 0,

        /**
         * @type {number}
         */
        shadowOffsetX: 0,

        /**
         * @type {number}
         */
        shadowOffsetY: 0,

        /**
         * @type {number}
         */
        lineWidth: 1,

        /**
         * If stroke ignore scale
         * @type {Boolean}
         */
        strokeNoScale: false,

        // Bounding rect text configuration
        // Not affected by element transform
        /**
         * @type {string}
         */
        text: null,

        /**
         * If `fontSize` or `fontFamily` exists, `font` will be reset by
         * `fontSize`, `fontStyle`, `fontWeight`, `fontFamily`.
         * So do not visit it directly in upper application (like echarts),
         * but use `contain/text#makeFont` instead.
         * @type {string}
         */
        font: null,

        /**
         * The same as font. Use font please.
         * @deprecated
         * @type {string}
         */
        textFont: null,

        /**
         * It helps merging respectively, rather than parsing an entire font string.
         * @type {string}
         */
        fontStyle: null,

        /**
         * It helps merging respectively, rather than parsing an entire font string.
         * @type {string}
         */
        fontWeight: null,

        /**
         * It helps merging respectively, rather than parsing an entire font string.
         * Should be 12 but not '12px'.
         * @type {number}
         */
        fontSize: null,

        /**
         * It helps merging respectively, rather than parsing an entire font string.
         * @type {string}
         */
        fontFamily: null,

        /**
         * Reserved for special functinality, like 'hr'.
         * @type {string}
         */
        textTag: null,

        /**
         * @type {string}
         */
        textFill: '#000',

        /**
         * @type {string}
         */
        textStroke: null,

        /**
         * @type {number}
         */
        textWidth: null,

        /**
         * Only for textBackground.
         * @type {number}
         */
        textHeight: null,

        /**
         * @type {number}
         */
        textLineWidth: 1,

        /**
         * @type {number}
         */
        textLineHeight: null,

        /**
         * 'inside', 'left', 'right', 'top', 'bottom'
         * [x, y]
         * Based on x, y of rect.
         * @type {string|Array.<number>}
         * @default 'inside'
         */
        textPosition: 'inside',

        /**
         * If not specified, use the boundingRect of a `displayable`.
         * @type {Object}
         */
        textRect: null,

        /**
         * [x, y]
         * @type {Array.<number>}
         */
        textOffset: null,

        /**
         * @type {string}
         */
        textAlign: null,

        /**
         * @type {string}
         */
        textVerticalAlign: null,

        /**
         * @type {number}
         */
        textDistance: 5,

        /**
         * @type {string}
         */
        textShadowColor: 'transparent',

        /**
         * @type {number}
         */
        textShadowBlur: 0,

        /**
         * @type {number}
         */
        textShadowOffsetX: 0,

        /**
         * @type {number}
         */
        textShadowOffsetY: 0,

        /**
         * @type {string}
         */
        textBoxShadowColor: 'transparent',

        /**
         * @type {number}
         */
        textBoxShadowBlur: 0,

        /**
         * @type {number}
         */
        textBoxShadowOffsetX: 0,

        /**
         * @type {number}
         */
        textBoxShadowOffsetY: 0,

        /**
         * Whether transform text.
         * Only useful in Path and Image element
         * @type {boolean}
         */
        transformText: false,

        /**
         * Text rotate around position of Path or Image
         * Only useful in Path and Image element and transformText is false.
         */
        textRotation: 0,

        /**
         * Text origin of text rotation, like [10, 40].
         * Based on x, y of rect.
         * Useful in label rotation of circular symbol.
         * By default, this origin is textPosition.
         * Can be 'center'.
         * @type {string|Array.<number>}
         */
        textOrigin: null,

        /**
         * @type {string}
         */
        textBackgroundColor: null,

        /**
         * @type {string}
         */
        textBorderColor: null,

        /**
         * @type {number}
         */
        textBorderWidth: 0,

        /**
         * @type {number}
         */
        textBorderRadius: 0,

        /**
         * Can be `2` or `[2, 4]` or `[2, 3, 4, 5]`
         * @type {number|Array.<number>}
         */
        textPadding: null,

        /**
         * Text styles for rich text.
         * @type {Object}
         */
        rich: null,

        /**
         * {outerWidth, outerHeight, ellipsis, placeholder}
         * @type {Object}
         */
        truncate: null,

        /**
         * https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation
         * @type {string}
         */
        blend: null,

        normalize: function () {
            textHelper.normalizeTextStyle(this);
        },

        /**
         * @param {CanvasRenderingContext2D} ctx
         */
        bind: function (ctx, el, prevEl) {
            var style = this;
            var prevStyle = prevEl && prevEl.style;
            var firstDraw = !prevStyle;

            for (var i = 0; i < STYLE_COMMON_PROPS.length; i++) {
                var prop = STYLE_COMMON_PROPS[i];
                var styleName = prop[0];

                if (firstDraw || style[styleName] !== prevStyle[styleName]) {
                    // FIXME Invalid property value will cause style leak from previous element.
                    ctx[styleName] = style[styleName] || prop[1];
                }
            }

            if ((firstDraw || style.fill !== prevStyle.fill)) {
                ctx.fillStyle = style.fill;
            }
            if ((firstDraw || style.stroke !== prevStyle.stroke)) {
                ctx.strokeStyle = style.stroke;
            }
            if ((firstDraw || style.opacity !== prevStyle.opacity)) {
                ctx.globalAlpha = style.opacity == null ? 1 : style.opacity;
            }

            if ((firstDraw || style.blend !== prevStyle.blend)) {
                ctx.globalCompositeOperation = style.blend || 'source-over';
            }
            if (this.hasStroke()) {
                var lineWidth = style.lineWidth;
                ctx.lineWidth = lineWidth / (
                    (this.strokeNoScale && el && el.getLineScale) ? el.getLineScale() : 1
                );
            }
        },

        hasFill: function () {
            var fill = this.fill;
            return fill != null && fill !== 'none';
        },

        hasStroke: function () {
            var stroke = this.stroke;
            return stroke != null && stroke !== 'none' && this.lineWidth > 0;
        },

        /**
         * Extend from other style
         * @param {zrender/graphic/Style} otherStyle
         * @param {boolean} overwrite true: overwrirte any way.
         *                            false: overwrite only when !target.hasOwnProperty
         *                            others: overwrite when property is not null/undefined.
         */
        extendFrom: function (otherStyle, overwrite) {
            if (otherStyle) {
                for (var name in otherStyle) {
                    if (otherStyle.hasOwnProperty(name)
                        && (overwrite === true
                            || (
                                overwrite === false
                                    ? !this.hasOwnProperty(name)
                                    : otherStyle[name] != null
                            )
                        )
                    ) {
                        this[name] = otherStyle[name];
                    }
                }
            }
        },

        /**
         * Batch setting style with a given object
         * @param {Object|string} obj
         * @param {*} [obj]
         */
        set: function (obj, value) {
            if (typeof obj === 'string') {
                this[obj] = value;
            }
            else {
                this.extendFrom(obj, true);
            }
        },

        /**
         * Clone
         * @return {zrender/graphic/Style} [description]
         */
        clone: function () {
            var newStyle = new this.constructor();
            newStyle.extendFrom(this, true);
            return newStyle;
        },

        getGradient: function (ctx, obj, rect) {
            var method = obj.type === 'radial' ? createRadialGradient : createLinearGradient;
            var canvasGradient = method(ctx, obj, rect);
            var colorStops = obj.colorStops;
            for (var i = 0; i < colorStops.length; i++) {
                canvasGradient.addColorStop(
                    colorStops[i].offset, colorStops[i].color
                );
            }
            return canvasGradient;
        }

    };

    var styleProto = Style.prototype;
    for (var i = 0; i < STYLE_COMMON_PROPS.length; i++) {
        var prop = STYLE_COMMON_PROPS[i];
        if (!(prop[0] in styleProto)) {
            styleProto[prop[0]] = prop[1];
        }
    }

    // Provide for others
    Style.getGradient = styleProto.getGradient;

    return Style;
});<commit_msg>tweak.<commit_after>/**
 * @module zrender/graphic/Style
 */
define(function (require) {

    var textHelper = require('./helper/text');

    var STYLE_COMMON_PROPS = [
        ['shadowBlur', 0], ['shadowOffsetX', 0], ['shadowOffsetY', 0], ['shadowColor', '#000'],
        ['lineCap', 'butt'], ['lineJoin', 'miter'], ['miterLimit', 10]
    ];

    // var SHADOW_PROPS = STYLE_COMMON_PROPS.slice(0, 4);
    // var LINE_PROPS = STYLE_COMMON_PROPS.slice(4);

    var Style = function (opts, host) {
        this.extendFrom(opts, false);
        this.host = host;
    };

    function createLinearGradient(ctx, obj, rect) {
        var x = obj.x == null ? 0 : obj.x;
        var x2 = obj.x2 == null ? 1 : obj.x2;
        var y = obj.y == null ? 0 : obj.y;
        var y2 = obj.y2 == null ? 0 : obj.y2;

        if (!obj.global) {
            x = x * rect.width + rect.x;
            x2 = x2 * rect.width + rect.x;
            y = y * rect.height + rect.y;
            y2 = y2 * rect.height + rect.y;
        }

        var canvasGradient = ctx.createLinearGradient(x, y, x2, y2);

        return canvasGradient;
    }

    function createRadialGradient(ctx, obj, rect) {
        var width = rect.width;
        var height = rect.height;
        var min = Math.min(width, height);

        var x = obj.x == null ? 0.5 : obj.x;
        var y = obj.y == null ? 0.5 : obj.y;
        var r = obj.r == null ? 0.5 : obj.r;
        if (!obj.global) {
            x = x * width + rect.x;
            y = y * height + rect.y;
            r = r * min;
        }

        var canvasGradient = ctx.createRadialGradient(x, y, 0, x, y, r);

        return canvasGradient;
    }

    Style.prototype = {

        constructor: Style,

        /**
         * @type {module:zrender/graphic/Displayable}
         */
        host: null,

        /**
         * @type {string}
         */
        fill: '#000000',

        /**
         * @type {string}
         */
        stroke: null,

        /**
         * @type {number}
         */
        opacity: 1,

        /**
         * @type {Array.<number>}
         */
        lineDash: null,

        /**
         * @type {number}
         */
        lineDashOffset: 0,

        /**
         * @type {number}
         */
        shadowBlur: 0,

        /**
         * @type {number}
         */
        shadowOffsetX: 0,

        /**
         * @type {number}
         */
        shadowOffsetY: 0,

        /**
         * @type {number}
         */
        lineWidth: 1,

        /**
         * If stroke ignore scale
         * @type {Boolean}
         */
        strokeNoScale: false,

        // Bounding rect text configuration
        // Not affected by element transform
        /**
         * @type {string}
         */
        text: null,

        /**
         * If `fontSize` or `fontFamily` exists, `font` will be reset by
         * `fontSize`, `fontStyle`, `fontWeight`, `fontFamily`.
         * So do not visit it directly in upper application (like echarts),
         * but use `contain/text#makeFont` instead.
         * @type {string}
         */
        font: null,

        /**
         * The same as font. Use font please.
         * @deprecated
         * @type {string}
         */
        textFont: null,

        /**
         * It helps merging respectively, rather than parsing an entire font string.
         * @type {string}
         */
        fontStyle: null,

        /**
         * It helps merging respectively, rather than parsing an entire font string.
         * @type {string}
         */
        fontWeight: null,

        /**
         * It helps merging respectively, rather than parsing an entire font string.
         * Should be 12 but not '12px'.
         * @type {number}
         */
        fontSize: null,

        /**
         * It helps merging respectively, rather than parsing an entire font string.
         * @type {string}
         */
        fontFamily: null,

        /**
         * Reserved for special functinality, like 'hr'.
         * @type {string}
         */
        textTag: null,

        /**
         * @type {string}
         */
        textFill: '#000',

        /**
         * @type {string}
         */
        textStroke: null,

        /**
         * @type {number}
         */
        textWidth: null,

        /**
         * Only for textBackground.
         * @type {number}
         */
        textHeight: null,

        /**
         * textStroke may be set as some color as a default
         * value in upper applicaion, where the default value
         * of textLineWidth should be 0 to make sure that
         * user can choose to do not use text stroke.
         * @type {number}
         */
        textLineWidth: 0,

        /**
         * @type {number}
         */
        textLineHeight: null,

        /**
         * 'inside', 'left', 'right', 'top', 'bottom'
         * [x, y]
         * Based on x, y of rect.
         * @type {string|Array.<number>}
         * @default 'inside'
         */
        textPosition: 'inside',

        /**
         * If not specified, use the boundingRect of a `displayable`.
         * @type {Object}
         */
        textRect: null,

        /**
         * [x, y]
         * @type {Array.<number>}
         */
        textOffset: null,

        /**
         * @type {string}
         */
        textAlign: null,

        /**
         * @type {string}
         */
        textVerticalAlign: null,

        /**
         * @type {number}
         */
        textDistance: 5,

        /**
         * @type {string}
         */
        textShadowColor: 'transparent',

        /**
         * @type {number}
         */
        textShadowBlur: 0,

        /**
         * @type {number}
         */
        textShadowOffsetX: 0,

        /**
         * @type {number}
         */
        textShadowOffsetY: 0,

        /**
         * @type {string}
         */
        textBoxShadowColor: 'transparent',

        /**
         * @type {number}
         */
        textBoxShadowBlur: 0,

        /**
         * @type {number}
         */
        textBoxShadowOffsetX: 0,

        /**
         * @type {number}
         */
        textBoxShadowOffsetY: 0,

        /**
         * Whether transform text.
         * Only useful in Path and Image element
         * @type {boolean}
         */
        transformText: false,

        /**
         * Text rotate around position of Path or Image
         * Only useful in Path and Image element and transformText is false.
         */
        textRotation: 0,

        /**
         * Text origin of text rotation, like [10, 40].
         * Based on x, y of rect.
         * Useful in label rotation of circular symbol.
         * By default, this origin is textPosition.
         * Can be 'center'.
         * @type {string|Array.<number>}
         */
        textOrigin: null,

        /**
         * @type {string}
         */
        textBackgroundColor: null,

        /**
         * @type {string}
         */
        textBorderColor: null,

        /**
         * @type {number}
         */
        textBorderWidth: 0,

        /**
         * @type {number}
         */
        textBorderRadius: 0,

        /**
         * Can be `2` or `[2, 4]` or `[2, 3, 4, 5]`
         * @type {number|Array.<number>}
         */
        textPadding: null,

        /**
         * Text styles for rich text.
         * @type {Object}
         */
        rich: null,

        /**
         * {outerWidth, outerHeight, ellipsis, placeholder}
         * @type {Object}
         */
        truncate: null,

        /**
         * https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation
         * @type {string}
         */
        blend: null,

        normalize: function () {
            textHelper.normalizeTextStyle(this);
        },

        /**
         * @param {CanvasRenderingContext2D} ctx
         */
        bind: function (ctx, el, prevEl) {
            var style = this;
            var prevStyle = prevEl && prevEl.style;
            var firstDraw = !prevStyle;

            for (var i = 0; i < STYLE_COMMON_PROPS.length; i++) {
                var prop = STYLE_COMMON_PROPS[i];
                var styleName = prop[0];

                if (firstDraw || style[styleName] !== prevStyle[styleName]) {
                    // FIXME Invalid property value will cause style leak from previous element.
                    ctx[styleName] = style[styleName] || prop[1];
                }
            }

            if ((firstDraw || style.fill !== prevStyle.fill)) {
                ctx.fillStyle = style.fill;
            }
            if ((firstDraw || style.stroke !== prevStyle.stroke)) {
                ctx.strokeStyle = style.stroke;
            }
            if ((firstDraw || style.opacity !== prevStyle.opacity)) {
                ctx.globalAlpha = style.opacity == null ? 1 : style.opacity;
            }

            if ((firstDraw || style.blend !== prevStyle.blend)) {
                ctx.globalCompositeOperation = style.blend || 'source-over';
            }
            if (this.hasStroke()) {
                var lineWidth = style.lineWidth;
                ctx.lineWidth = lineWidth / (
                    (this.strokeNoScale && el && el.getLineScale) ? el.getLineScale() : 1
                );
            }
        },

        hasFill: function () {
            var fill = this.fill;
            return fill != null && fill !== 'none';
        },

        hasStroke: function () {
            var stroke = this.stroke;
            return stroke != null && stroke !== 'none' && this.lineWidth > 0;
        },

        /**
         * Extend from other style
         * @param {zrender/graphic/Style} otherStyle
         * @param {boolean} overwrite true: overwrirte any way.
         *                            false: overwrite only when !target.hasOwnProperty
         *                            others: overwrite when property is not null/undefined.
         */
        extendFrom: function (otherStyle, overwrite) {
            if (otherStyle) {
                for (var name in otherStyle) {
                    if (otherStyle.hasOwnProperty(name)
                        && (overwrite === true
                            || (
                                overwrite === false
                                    ? !this.hasOwnProperty(name)
                                    : otherStyle[name] != null
                            )
                        )
                    ) {
                        this[name] = otherStyle[name];
                    }
                }
            }
        },

        /**
         * Batch setting style with a given object
         * @param {Object|string} obj
         * @param {*} [obj]
         */
        set: function (obj, value) {
            if (typeof obj === 'string') {
                this[obj] = value;
            }
            else {
                this.extendFrom(obj, true);
            }
        },

        /**
         * Clone
         * @return {zrender/graphic/Style} [description]
         */
        clone: function () {
            var newStyle = new this.constructor();
            newStyle.extendFrom(this, true);
            return newStyle;
        },

        getGradient: function (ctx, obj, rect) {
            var method = obj.type === 'radial' ? createRadialGradient : createLinearGradient;
            var canvasGradient = method(ctx, obj, rect);
            var colorStops = obj.colorStops;
            for (var i = 0; i < colorStops.length; i++) {
                canvasGradient.addColorStop(
                    colorStops[i].offset, colorStops[i].color
                );
            }
            return canvasGradient;
        }

    };

    var styleProto = Style.prototype;
    for (var i = 0; i < STYLE_COMMON_PROPS.length; i++) {
        var prop = STYLE_COMMON_PROPS[i];
        if (!(prop[0] in styleProto)) {
            styleProto[prop[0]] = prop[1];
        }
    }

    // Provide for others
    Style.getGradient = styleProto.getGradient;

    return Style;
});<|endoftext|>


<commit_before>import d3 from 'd3'
import logger from '../logger'

const formatCandleData = (data) => data.map(d => ({
  begins_at: d.begins_at,
  open_price: parseFloat(d.open_price),
  close_price: parseFloat(d.close_price),
  high_price: parseFloat(d.high_price),
  low_price: parseFloat(d.low_price),
  volume: d.volume,
  interpolated: d.interpolated,
}))

const parseCandleData = (chartData) => {
  const formattedData = formatCandleData(chartData.data)
  const isTrendingUp = (chartData.displayPrevClose
          ? formattedData[formattedData.length - 1].open_price > chartData.prevClose
          : formattedData[formattedData.length - 1].close_price > formattedData[0].close_price)

  return {
    data: formattedData,
    minValue: Math.min(chartData.prevClose || Number.MAX_VALUE,
      Math.min(...formattedData.map(d => Math.min(d.open_price, d.close_price)))),
    maxValue: Math.max(chartData.prevClose || Number.MIN_VALUE,
      Math.max(...formattedData.map(d => Math.max(d.open_price, d.close_price)))),
    klass: isTrendingUp ? 'quote-up' : 'quote-down',
  }
}

/** Draws a solid gray rectangle in the svg so the candle chart can be dragged around */
const drawFill = (svg) => {
  const g = svg.selectAll('g.fill-group')
    .data(['1'])
  g.enter().append('g')
      .attr('class', 'fill-group')
  const fill = g.selectAll('.fill')
    .data(['1'])
  fill.enter().append('rect')
      .attr('class', 'fill')
      .attr('width', '100%')
      .attr('height', '100%')
}

const drawCandleChart = (chartData) => {
  const metadata = parseCandleData(chartData)
  const width = d3.select(chartData.selector).node().getBoundingClientRect().width
  const height = chartData.height
  const candleMargin = 2
  const minCandleWidth = 5
  const candleWidth = minCandleWidth
  const x = d3.scale.linear()
    .domain([0, metadata.data.length])
    .range([0, width])
    // .clamp(true)
    .nice()
  const y = d3.scale.linear()
    .domain([metadata.minValue, metadata.maxValue])
    .range([height, 0])
    // .clamp(true)
    .nice()
  const xAxis = d3.svg.axis()
    .scale(x)
    .orient("bottom")
    .tickSize(-height)
    .tickFormat(d3.format("s"))
  const onZoom = () => {
    svg.select(".x.axis").call(xAxis)
    drawCandles(candlesContainer)
  }
  const zoom = d3.behavior.zoom()
    .x(x)
    .y(y)
    .scaleExtent([1, 1])
    .on('zoom', onZoom)
  const candleHeight = (d) => Math.abs(
    y(Math.min(d.open_price, d.close_price)) - y(Math.max(d.open_price, d.close_price)))
  const candleClass = (d) => (d.open_price < d.close_price ? 'quote-up' : 'quote-down')
  const drawCandles = (candlesContainer) => {
    candlesContainer.selectAll('*').remove()
    candlesContainer.selectAll("line.stem")
      .data(metadata.data)
      .enter()
      .append("line")
      .attr("class", "stem")
      .attr("x1", (d, i) => x(i))
      .attr("x2", (d, i) => x(i))
      .attr("y1", (d) => y(d.high_price))
      .attr("y2", (d) => y(d.low_price))
      .attr("stroke", () => 'black')
      .attr("transform", (d, i) =>
        `translate(${(i * candleMargin) + (candleWidth / 2)}, 0)`)
    candlesContainer.selectAll("rect")
      .data(metadata.data)
      .enter()
      .append("rect")
      .attr("class", (d) => `candle ${candleClass(d)}`)
      .attr("x", (d, i) => x(i))
      .attr("y", (d) => y(Math.max(d.open_price, d.close_price)))
      .attr("width", () => candleWidth)
      .attr("height", (d) => candleHeight(d))
      .attr("transform", (d, i) => `translate(${i * candleMargin}, 0)`)
  }
  d3.selectAll(`${chartData.selector} > *`).remove()
  const svgNode = d3.select(chartData.selector)
      .append("svg")
      .attr('class', `chart candle`)
      .attr("width", chartData.width)
      .attr("height", height)
      .call(zoom)
  const svg = svgNode.append('g')
  drawFill(svg)
  svg.append("g")
    .attr("class", "x axis")
    .attr("transform", `translate(0, ${height - 25})`)
    .call(xAxis)
  const candlesContainer = svg.append('g')
  drawCandles(candlesContainer)
}

export { parseCandleData, drawCandleChart }
<commit_msg>Use time scale for graph X axis<commit_after>import d3 from 'd3'
import logger from '../logger'

const formatCandleData = (data) => data.map(d => ({
  begins_at: d.begins_at,
  open_price: parseFloat(d.open_price),
  close_price: parseFloat(d.close_price),
  high_price: parseFloat(d.high_price),
  low_price: parseFloat(d.low_price),
  volume: d.volume,
  interpolated: d.interpolated,
}))

const parseCandleData = (chartData) => {
  const formattedData = formatCandleData(chartData.data)
  const isTrendingUp = (chartData.displayPrevClose
          ? formattedData[formattedData.length - 1].open_price > chartData.prevClose
          : formattedData[formattedData.length - 1].close_price > formattedData[0].close_price)

  return {
    data: formattedData,
    minValue: Math.min(chartData.prevClose || Number.MAX_VALUE,
      Math.min(...formattedData.map(d => Math.min(d.open_price, d.close_price)))),
    maxValue: Math.max(chartData.prevClose || Number.MIN_VALUE,
      Math.max(...formattedData.map(d => Math.max(d.open_price, d.close_price)))),
    klass: isTrendingUp ? 'quote-up' : 'quote-down',
  }
}

/** Draws a solid gray rectangle in the svg so the candle chart can be dragged around */
const drawFill = (svg) => {
  const g = svg.selectAll('g.fill-group')
    .data(['1'])
  g.enter().append('g')
      .attr('class', 'fill-group')
  const fill = g.selectAll('.fill')
    .data(['1'])
  fill.enter().append('rect')
      .attr('class', 'fill')
      .attr('width', '100%')
      .attr('height', '100%')
}

const drawCandleChart = (chartData) => {
  const metadata = parseCandleData(chartData)
  const height = chartData.height
  const candleMargin = 2
  const minCandleWidth = 6
  const totalItems = metadata.data.length
  const candleWidth = minCandleWidth
  const stringifyDatetime = d3.time.format('%Y-%m-%dT%H:%M:%SZ')
  const parseDatetime = stringifyDatetime.parse
  const formatXAxis = d3.time.format('%b/%y')
  const start = metadata.data[0].begins_at
  const end = metadata.data[totalItems - 1].begins_at
  const getX = (d) => parseDatetime(d.begins_at)
  logger.log(`start: ${parseDatetime(start)}, end: ${parseDatetime(end)}`)
  const x = d3.time.scale()
    .domain([parseDatetime(start), parseDatetime(end)])
    .range([0, totalItems * ((candleWidth * 2) + candleMargin)])
    .nice()
  const y = d3.scale.linear()
    .domain([metadata.minValue, metadata.maxValue])
    .range([height, 0])
    .nice()
  const xAxis = d3.svg.axis()
    .scale(x)
    .orient("bottom")
    .tickSize(-height)
    .tickFormat(formatXAxis)
  const onZoom = () => {
    svg.select(".x.axis").call(xAxis)
    candlesContainer.attr('transform', `translate(${d3.event.translate[0]},0)`)
  }
  const zoom = d3.behavior.zoom()
    .x(x)
    .y(y)
    .scaleExtent([1, 1])
    .on('zoom', onZoom)
  const candleHeight = (d) => Math.abs(
    y(Math.min(d.open_price, d.close_price)) - y(Math.max(d.open_price, d.close_price)))
  const candleClass = (d) => (d.open_price < d.close_price ? 'quote-up' : 'quote-down')
  const drawCandles = (candlesContainer) => {
    candlesContainer.selectAll('*').remove()
    candlesContainer.selectAll("line.stem")
      .data(metadata.data)
      .enter()
      .append("line")
      .attr("class", "stem")
      .attr("x1", (d) => x(getX(d)))
      .attr("x2", (d) => x(getX(d)))
      .attr("y1", (d) => y(d.high_price))
      .attr("y2", (d) => y(d.low_price))
      .attr("stroke", () => 'black')
      .attr("transform", () => `translate(${candleWidth / 2}, 0)`)
    candlesContainer.selectAll("rect")
      .data(metadata.data)
      .enter()
      .append("rect")
      .attr("class", (d) => `candle ${candleClass(d)}`)
      .attr("x", (d) => x(getX(d)))
      .attr("y", (d) => y(Math.max(d.open_price, d.close_price)))
      .attr("width", () => candleWidth)
      .attr("height", (d) => candleHeight(d))
  }
  d3.selectAll(`${chartData.selector} > *`).remove()
  const svgNode = d3.select(chartData.selector)
      .append("svg")
      .attr('class', `chart candle`)
      .attr("width", chartData.width)
      .attr("height", height)
      .call(zoom)
  const svg = svgNode.append('g')
  drawFill(svg)
  svg.append("g")
    .attr("class", "x axis")
    .attr("transform", `translate(0, ${height - 25})`)
    .call(xAxis)
  const candlesContainer = svg.append('g')
  drawCandles(candlesContainer)
}

export { parseCandleData, drawCandleChart }
<|endoftext|>


<commit_before>#!/usr/bin/env node

// TODO(vojta): pre-commit hook for validating messages
// TODO(vojta): report errors, currently Q silence everything which really sucks

'use strict';

var child = require('child_process');
var fs = require('fs');
var util = require('util');
var q = require('qq');

var GIT_LOG_CMD = 'git log --grep="%s" -E --format=%s %s..HEAD';
var GIT_TAG_CMD = 'git describe --tags --abbrev=0';

var HEADER_TPL = '\n# %s (%s)\n\n';
var LINK_ISSUE = '[#%s](https://github.com/angular/angular.js/issues/%s)';
var LINK_COMMIT = '[%s](https://github.com/angular/angular.js/commit/%s)';

var EMPTY_COMPONENT = '$$';

var warn = function() {
  console.log('WARNING:', util.format.apply(null, arguments));
};

var parseRawCommit = function(raw) {
  if (!raw) return null;

  var lines = raw.split('\n');
  var msg = {}, match;

  msg.hash = lines.shift();
  msg.subject = lines.shift();
  msg.closes = [];
  msg.breaks = [];

  lines.forEach(function(line) {
    match = line.match(/(?:Closes|Fixes)\s#(\d+)/);
    if (match) msg.closes.push(parseInt(match[1]));
  });

  match = raw.match(/BREAKING CHANGE:([\s\S]*)/);
  if (match) {
    msg.breaking = match[1];
  }

  msg.body = lines.join('\n');
  match = msg.subject.match(/^(.*)\((.*)\)\:\s(.*)$/);

  if (!match || !match[1] || !match[3]) {
    warn('Incorrect message: %s %s', msg.hash, msg.subject);
    return null;
  }

  msg.type = match[1];
  msg.component = match[2];
  msg.subject = match[3];

  return msg;
};

var linkToIssue = function(issue) {
  return util.format(LINK_ISSUE, issue, issue);
};

var linkToCommit = function(hash) {
  return util.format(LINK_COMMIT, hash.substr(0, 8), hash);
};

var currentDate = function() {
  var now = new Date();
  var pad = function(i) {
    return ('0' + i).substr(-2);
  };

  return util.format('%d-%s-%s', now.getFullYear(), pad(now.getMonth() + 1), pad(now.getDate()));
};

var printSection = function(stream, title, section, printCommitLinks) {
  printCommitLinks = printCommitLinks === undefined ? true : printCommitLinks;
  var components = Object.getOwnPropertyNames(section).sort();

  if (!components.length) return;

  stream.write(util.format('\n## %s\n\n', title));

  components.forEach(function(name) {
    var prefix = '-';
    var nested = section[name].length > 1;

    if (name !== EMPTY_COMPONENT) {
      if (nested) {
        stream.write(util.format('- **%s:**\n', name));
        prefix = '  -';
      } else {
        prefix = util.format('- **%s:**', name);
      }
    }

    section[name].forEach(function(commit) {
      if (printCommitLinks) {
        stream.write(util.format('%s %s\n  (%s', prefix, commit.subject, linkToCommit(commit.hash)));
        if (commit.closes.length) {
          stream.write(',\n   ' + commit.closes.map(linkToIssue).join(', '));
        }
        stream.write(')\n');
      } else {
        stream.write(util.format('%s %s\n', prefix, commit.subject));
      }
    });
  });

  stream.write('\n');
};

var readGitLog = function(grep, from) {
  var deferred = q.defer();

  // TODO(vojta): if it's slow, use spawn and stream it instead
  child.exec(util.format(GIT_LOG_CMD, grep, '%H%n%s%n%b%n==END==', from), function(code, stdout, stderr) {
    var commits = [];

    stdout.split('\n==END==\n').forEach(function(rawCommit) {
      var commit = parseRawCommit(rawCommit);
      if (commit) commits.push(commit);
    });

    deferred.resolve(commits);
  });

  return deferred.promise;
};

var writeChangelog = function(stream, commits, version) {
  var sections = {
    fix: {},
    feat: {},
    perf: {},
    breaks: {}
  };

  sections.breaks[EMPTY_COMPONENT] = [];

  commits.forEach(function(commit) {
    var section = sections[commit.type];
    var component = commit.component || EMPTY_COMPONENT;

    if (section) {
      section[component] = section[component] || [];
      section[component].push(commit);
    }

    if (commit.breaking) {
      sections.breaks[component] = sections.breaks[component] || [];
      sections.breaks[component].push({
        subject: util.format("due to %s,\n %s", linkToCommit(commit.hash), commit.breaking),
        hash: commit.hash,
        closes: []
      });
    }
  });

  stream.write(util.format(HEADER_TPL, version, version, currentDate()));
  printSection(stream, 'Bug Fixes', sections.fix);
  printSection(stream, 'Features', sections.feat);
  printSection(stream, 'Performance Improvements', sections.perf);
  printSection(stream, 'Breaking Changes', sections.breaks, false);
};

var getPreviousTag = function() {
  var deferred = q.defer();
  child.exec(GIT_TAG_CMD, function(code, stdout, stderr) {
    if (code) deferred.reject('Cannot get the previous tag.');
    else deferred.resolve(stdout.replace('\n', ''));
  });
  return deferred.promise;
};

var generate = function(version, file) {

  getPreviousTag().then(function(tag) {
    console.log('Reading git log since', tag);
    readGitLog('^fix|^feat|^perf|BREAKING', tag).then(function(commits) {
      console.log('Parsed', commits.length, 'commits');
      console.log('Generating changelog to', file || 'stdout', '(', version, ')');
      writeChangelog(file ? fs.createWriteStream(file) : process.stdout, commits, version);
    });
  });
};

// publish for testing
exports.parseRawCommit = parseRawCommit;
exports.printSection = printSection;

// hacky start if not run by jasmine :-D
if (process.argv.join('').indexOf('jasmine-node') === -1) {
  generate(process.argv[2], process.argv[3]);
}
<commit_msg>chore(changelog.js): don't add empty breaking change section<commit_after>#!/usr/bin/env node

// TODO(vojta): pre-commit hook for validating messages
// TODO(vojta): report errors, currently Q silence everything which really sucks

'use strict';

var child = require('child_process');
var fs = require('fs');
var util = require('util');
var q = require('qq');

var GIT_LOG_CMD = 'git log --grep="%s" -E --format=%s %s..HEAD';
var GIT_TAG_CMD = 'git describe --tags --abbrev=0';

var HEADER_TPL = '\n# %s (%s)\n\n';
var LINK_ISSUE = '[#%s](https://github.com/angular/angular.js/issues/%s)';
var LINK_COMMIT = '[%s](https://github.com/angular/angular.js/commit/%s)';

var EMPTY_COMPONENT = '$$';

var warn = function() {
  console.log('WARNING:', util.format.apply(null, arguments));
};

var parseRawCommit = function(raw) {
  if (!raw) return null;

  var lines = raw.split('\n');
  var msg = {}, match;

  msg.hash = lines.shift();
  msg.subject = lines.shift();
  msg.closes = [];
  msg.breaks = [];

  lines.forEach(function(line) {
    match = line.match(/(?:Closes|Fixes)\s#(\d+)/);
    if (match) msg.closes.push(parseInt(match[1]));
  });

  match = raw.match(/BREAKING CHANGE:([\s\S]*)/);
  if (match) {
    msg.breaking = match[1];
  }

  msg.body = lines.join('\n');
  match = msg.subject.match(/^(.*)\((.*)\)\:\s(.*)$/);

  if (!match || !match[1] || !match[3]) {
    warn('Incorrect message: %s %s', msg.hash, msg.subject);
    return null;
  }

  msg.type = match[1];
  msg.component = match[2];
  msg.subject = match[3];

  return msg;
};

var linkToIssue = function(issue) {
  return util.format(LINK_ISSUE, issue, issue);
};

var linkToCommit = function(hash) {
  return util.format(LINK_COMMIT, hash.substr(0, 8), hash);
};

var currentDate = function() {
  var now = new Date();
  var pad = function(i) {
    return ('0' + i).substr(-2);
  };

  return util.format('%d-%s-%s', now.getFullYear(), pad(now.getMonth() + 1), pad(now.getDate()));
};

var printSection = function(stream, title, section, printCommitLinks) {
  printCommitLinks = printCommitLinks === undefined ? true : printCommitLinks;
  var components = Object.getOwnPropertyNames(section).sort();

  if (!components.length) return;

  stream.write(util.format('\n## %s\n\n', title));

  components.forEach(function(name) {
    var prefix = '-';
    var nested = section[name].length > 1;

    if (name !== EMPTY_COMPONENT) {
      if (nested) {
        stream.write(util.format('- **%s:**\n', name));
        prefix = '  -';
      } else {
        prefix = util.format('- **%s:**', name);
      }
    }

    section[name].forEach(function(commit) {
      if (printCommitLinks) {
        stream.write(util.format('%s %s\n  (%s', prefix, commit.subject, linkToCommit(commit.hash)));
        if (commit.closes.length) {
          stream.write(',\n   ' + commit.closes.map(linkToIssue).join(', '));
        }
        stream.write(')\n');
      } else {
        stream.write(util.format('%s %s\n', prefix, commit.subject));
      }
    });
  });

  stream.write('\n');
};

var readGitLog = function(grep, from) {
  var deferred = q.defer();

  // TODO(vojta): if it's slow, use spawn and stream it instead
  child.exec(util.format(GIT_LOG_CMD, grep, '%H%n%s%n%b%n==END==', from), function(code, stdout, stderr) {
    var commits = [];

    stdout.split('\n==END==\n').forEach(function(rawCommit) {
      var commit = parseRawCommit(rawCommit);
      if (commit) commits.push(commit);
    });

    deferred.resolve(commits);
  });

  return deferred.promise;
};

var writeChangelog = function(stream, commits, version) {
  var sections = {
    fix: {},
    feat: {},
    perf: {},
    breaks: {}
  };

  commits.forEach(function(commit) {
    var section = sections[commit.type];
    var component = commit.component || EMPTY_COMPONENT;

    if (section) {
      section[component] = section[component] || [];
      section[component].push(commit);
    }

    if (commit.breaking) {
      sections.breaks[component] = sections.breaks[component] || [];
      sections.breaks[component].push({
        subject: util.format("due to %s,\n %s", linkToCommit(commit.hash), commit.breaking),
        hash: commit.hash,
        closes: []
      });
    }
  });

  stream.write(util.format(HEADER_TPL, version, version, currentDate()));
  printSection(stream, 'Bug Fixes', sections.fix);
  printSection(stream, 'Features', sections.feat);
  printSection(stream, 'Performance Improvements', sections.perf);
  printSection(stream, 'Breaking Changes', sections.breaks, false);
};

var getPreviousTag = function() {
  var deferred = q.defer();
  child.exec(GIT_TAG_CMD, function(code, stdout, stderr) {
    if (code) deferred.reject('Cannot get the previous tag.');
    else deferred.resolve(stdout.replace('\n', ''));
  });
  return deferred.promise;
};

var generate = function(version, file) {

  getPreviousTag().then(function(tag) {
    console.log('Reading git log since', tag);
    readGitLog('^fix|^feat|^perf|BREAKING', tag).then(function(commits) {
      console.log('Parsed', commits.length, 'commits');
      console.log('Generating changelog to', file || 'stdout', '(', version, ')');
      writeChangelog(file ? fs.createWriteStream(file) : process.stdout, commits, version);
    });
  });
};

// publish for testing
exports.parseRawCommit = parseRawCommit;
exports.printSection = printSection;

// hacky start if not run by jasmine :-D
if (process.argv.join('').indexOf('jasmine-node') === -1) {
  generate(process.argv[2], process.argv[3]);
}
<|endoftext|>


<commit_before>/**
 * This is the base config for all builds. It uses env.buildtype to change
 * the output configuration for different build types.
 */

var webpack = require('webpack')
var autoprefixer = require('autoprefixer')
var join = require('path').join
var _ = require('lodash')
var stylelint = require('stylelint')
var ExtractTextPlugin = require('extract-text-webpack-plugin')
var postcssImport = require('postcss-import')
var postcssCustomProperties = require('postcss-custom-properties')
var postcssCalc = require('postcss-calc')
var postcssColorFunction = require('postcss-color-function')
var postcssCustomMedia = require('postcss-custom-media')
var postcssEsplit = require('postcss-esplit')
// var postcssBemLinter = require('postcss-bem-linter')
var ReactIntlAggregatePlugin = require('react-intl-aggregate-webpack-plugin')
var ReactIntlFlattenPlugin = require('react-intl-flatten-webpack-plugin')

/* Helper so we can use ternary with undefined to not specify a key */
function dropUndef (obj) {
  return _(obj).omitBy(_.isNil).value()
}

var postCssLoader = {
  loader: 'postcss-loader',
  options: {
    plugins: [
      postcssImport(),
      postcssCustomProperties,
      postcssCalc,
      postcssColorFunction,
      postcssCustomMedia,
      postcssEsplit({
        quiet: true
      }),

      /*
       * This is not called with each imported file, but only with top-level
       * files. Some work is needed before this will give useful output.
       */
      // postcssBemLinter({
      //   preset: 'suit',
      //   implicitComponents: [
      //     '**/components/**/*.css',
      //     '**/containers/**/*.css'
      //   ],
      //   implicitUtilities: [
      //     '**/editor/css/**/*.css'
      //   ]
      // }),

      autoprefixer({
        browsers: [
          'Explorer >= 9',
          'last 2 Chrome versions',
          'last 2 Firefox versions',
          'last 2 Safari versions',
          'last 2 iOS versions',
          'Android 4'
        ]
      })
    ]
  }
}

/*
 * To set env on command line:
 *   webpack --env.buildtype=draft
 *
 * To set env in build scripts, just pass it as a normal function argument:
 *   import createConfig from '../webpack.config'
 *   const config = createConfig({ buildtype: 'draft' })
 *
 * Valid buildtype settings: prod (default), draft, dev, storybook.
 *
 * More info:
 *   https://blog.flennik.com/the-fine-art-of-the-webpack-2-config-dc4d19d7f172
 */
module.exports = function (env) {
  var buildtype = env && env.buildtype || 'prod'

  /**
   * Development build.
   *
   * This build is meant to use with webpack-dev-server for hot redeployment. It
   * should be optimised primarily for in-browser debugging, then for fast
   * incremental builds.
   */
  var dev = buildtype === 'dev'

  /**
   * Storybook config.
   *
   * Used by storybook to build individual components. Should inline the css and
   * optimize for fast incremental builds.
   */
  var storybook = buildtype === 'storybook'

  /**
   * Draft build (the fast build).
   *
   * This build is intended to build as fast as possible, while being
   * semantically the same as the production build.
   *
   * The output is not optimised for artifact size, human-readability or to work
   * well with development tools or debuggers. Use the prod or dev config if you
   * want those properties in the output.
   */
  var draft = buildtype === 'draft'

  /**
   * Production build config.
   *
   * This should be optimised for production performance and a small download.
   * Builds with this config should fail on any error, including linting errors.
   */
  var prod = buildtype === 'prod'

  // several options have the same values for both draft and prod
  var fullBuild = draft || prod

  return dropUndef({
    entry: storybook ? undefined : dropUndef({
      'frontend': './app/index',
      'editor': './app/editor/index.js',
      'frontend.legacy': fullBuild ? './app/legacy' : undefined
    }),
    output: storybook ? undefined : dropUndef({
      path: join(__dirname, 'dist'),
      filename: fullBuild ? '[name].min.js' : '[name].js',
      chunkFilename: fullBuild ? '[name].min.js' : '[name].js',
      // includes comments in the generated code about where the code came from
      pathinfo: dev,
      // required for hot module replacement
      publicPath: dev ? 'http://localhost:8000/' : undefined
    }),
    module: {
      rules: _.compact([
        /* Checks for errors in syntax, and for problematic and inconsistent
        * code in all JavaScript files.
        * Configured in .eslintrc
        */
        // TODO consider turning on for storybook
        storybook ? undefined : {
          test: /\.jsx?$/,
          exclude: /node_modules/,
          enforce: 'pre',
          loader: 'eslint-loader',
          options: {
            failOnWarning: !dev,
            failOnError: !dev
          }
        },

        /* Allows use of newer javascript syntax.
         *  - mainly ES6/ES2015 syntax, and a few ES2016 things
         *  - configured in .babelrc
         */
        {
          test: /\.jsx?$/,
          exclude: /node_modules/,
          include: join(__dirname, 'app'),
          loader: 'babel-loader',
          options: {
            babelrc: true
          }
        },

        /* Bundles all the css and allows use of various niceties, including
         * imports, variables, calculations, and non-prefixed codes.
         */
        {
          test: /\.css$/,
          use: ExtractTextPlugin.extract({
            fallback: 'style-loader',
            use: _.compact([
              {
                loader: 'css-loader',
                options: {
                  minimize: prod,
                }
              },
              draft ? undefined : 'csso-loader',
              postCssLoader
            ])
          })
        },

        /* Bundles bootstrap css into the same bundle as the other css.
         * TODO look at running through csso, same as other css
         */
        {
          test: /\.less$/,
          exclude: /node_modules/,
          use: ExtractTextPlugin.extract({
            fallback: 'style-loader',
            use: [
              {
                loader: 'postcss-loader',
                options: {
                  plugins: [
                    require('stylelint'),
                  ]
                }
              },
              postCssLoader,
              'less-loader'
            ]
          })
        }
      ])
    },

    plugins: _.compact([
      /* Outputs css to a separate file per entry-point.
         Note the call to .extract above */
      new ExtractTextPlugin({
        filename: '[name].css',
        // storybook should use the fallback: style-loader
        disable: storybook
      }),
      new webpack.NoEmitOnErrorsPlugin(),

      prod
        ? new webpack.optimize.UglifyJsPlugin({ sourceMap: true })
        : undefined,
      prod
        // Workaround to switch old loaders to minimize mode
        ? new webpack.LoaderOptionsPlugin({ minimize: true })
        : undefined,

      new webpack.DefinePlugin({
        'process.env': {
          'NODE_ENV': JSON.stringify(
            dev || storybook ? 'development' : 'production')
        }
      }),

      // Prepare source strings for translation.
      // This puts the strings generated by react-intl into a single file.
      fullBuild
        ? new ReactIntlAggregatePlugin({
          messagesPattern: join(__dirname, 'build/messages/**/*.json'),
          aggregateOutputDir: join(__dirname, 'messages'),
          aggregateFilename: 'en-us'
        })
        : undefined,

      // Convert source (en-US) and translated strings to the format the app
      // can consume, in the dist directory.
      fullBuild
        ? new ReactIntlFlattenPlugin({
          aggregatePattern: join(__dirname, 'messages/*.json'),
          // dist/messages/[locale-id].json
          langOutputDir: 'messages'
        })
        : undefined
    ]),

    resolve: {
      /* Subdirectories to check while searching up tree for module
       * Default is ['', '.js'] */
      extensions: ['.js', '.jsx', '.json', '.css', '.less']
    },

    node: {
      __dirname: true
    },

    /* Caching for incremental builds, only needed for dev mode */
    cache: !fullBuild,

    /* fail on first error */
    bail: fullBuild,

    devtool: prod ? 'source-map' : 'eval'
  })
}
<commit_msg>fix(ZNTA-2231) added missing option to css-loader in webpack<commit_after>/**
 * This is the base config for all builds. It uses env.buildtype to change
 * the output configuration for different build types.
 */

var webpack = require('webpack')
var autoprefixer = require('autoprefixer')
var join = require('path').join
var _ = require('lodash')
var stylelint = require('stylelint')
var ExtractTextPlugin = require('extract-text-webpack-plugin')
var postcssImport = require('postcss-import')
var postcssCustomProperties = require('postcss-custom-properties')
var postcssCalc = require('postcss-calc')
var postcssColorFunction = require('postcss-color-function')
var postcssCustomMedia = require('postcss-custom-media')
var postcssEsplit = require('postcss-esplit')
// var postcssBemLinter = require('postcss-bem-linter')
var ReactIntlAggregatePlugin = require('react-intl-aggregate-webpack-plugin')
var ReactIntlFlattenPlugin = require('react-intl-flatten-webpack-plugin')

/* Helper so we can use ternary with undefined to not specify a key */
function dropUndef (obj) {
  return _(obj).omitBy(_.isNil).value()
}

var postCssLoader = {
  loader: 'postcss-loader',
  options: {
    plugins: [
      postcssImport(),
      postcssCustomProperties,
      postcssCalc,
      postcssColorFunction,
      postcssCustomMedia,
      postcssEsplit({
        quiet: true
      }),

      /*
       * This is not called with each imported file, but only with top-level
       * files. Some work is needed before this will give useful output.
       */
      // postcssBemLinter({
      //   preset: 'suit',
      //   implicitComponents: [
      //     '**/components/**/*.css',
      //     '**/containers/**/*.css'
      //   ],
      //   implicitUtilities: [
      //     '**/editor/css/**/*.css'
      //   ]
      // }),

      autoprefixer({
        browsers: [
          'Explorer >= 9',
          'last 2 Chrome versions',
          'last 2 Firefox versions',
          'last 2 Safari versions',
          'last 2 iOS versions',
          'Android 4'
        ]
      })
    ]
  }
}

/*
 * To set env on command line:
 *   webpack --env.buildtype=draft
 *
 * To set env in build scripts, just pass it as a normal function argument:
 *   import createConfig from '../webpack.config'
 *   const config = createConfig({ buildtype: 'draft' })
 *
 * Valid buildtype settings: prod (default), draft, dev, storybook.
 *
 * More info:
 *   https://blog.flennik.com/the-fine-art-of-the-webpack-2-config-dc4d19d7f172
 */
module.exports = function (env) {
  var buildtype = env && env.buildtype || 'prod'

  /**
   * Development build.
   *
   * This build is meant to use with webpack-dev-server for hot redeployment. It
   * should be optimised primarily for in-browser debugging, then for fast
   * incremental builds.
   */
  var dev = buildtype === 'dev'

  /**
   * Storybook config.
   *
   * Used by storybook to build individual components. Should inline the css and
   * optimize for fast incremental builds.
   */
  var storybook = buildtype === 'storybook'

  /**
   * Draft build (the fast build).
   *
   * This build is intended to build as fast as possible, while being
   * semantically the same as the production build.
   *
   * The output is not optimised for artifact size, human-readability or to work
   * well with development tools or debuggers. Use the prod or dev config if you
   * want those properties in the output.
   */
  var draft = buildtype === 'draft'

  /**
   * Production build config.
   *
   * This should be optimised for production performance and a small download.
   * Builds with this config should fail on any error, including linting errors.
   */
  var prod = buildtype === 'prod'

  // several options have the same values for both draft and prod
  var fullBuild = draft || prod

  return dropUndef({
    entry: storybook ? undefined : dropUndef({
      'frontend': './app/index',
      'editor': './app/editor/index.js',
      'frontend.legacy': fullBuild ? './app/legacy' : undefined
    }),
    output: storybook ? undefined : dropUndef({
      path: join(__dirname, 'dist'),
      filename: fullBuild ? '[name].min.js' : '[name].js',
      chunkFilename: fullBuild ? '[name].min.js' : '[name].js',
      // includes comments in the generated code about where the code came from
      pathinfo: dev,
      // required for hot module replacement
      publicPath: dev ? 'http://localhost:8000/' : undefined
    }),
    module: {
      rules: _.compact([
        /* Checks for errors in syntax, and for problematic and inconsistent
        * code in all JavaScript files.
        * Configured in .eslintrc
        */
        // TODO consider turning on for storybook
        storybook ? undefined : {
          test: /\.jsx?$/,
          exclude: /node_modules/,
          enforce: 'pre',
          loader: 'eslint-loader',
          options: {
            failOnWarning: !dev,
            failOnError: !dev
          }
        },

        /* Allows use of newer javascript syntax.
         *  - mainly ES6/ES2015 syntax, and a few ES2016 things
         *  - configured in .babelrc
         */
        {
          test: /\.jsx?$/,
          exclude: /node_modules/,
          include: join(__dirname, 'app'),
          loader: 'babel-loader',
          options: {
            babelrc: true
          }
        },

        /* Bundles all the css and allows use of various niceties, including
         * imports, variables, calculations, and non-prefixed codes.
         */
        {
          test: /\.css$/,
          use: ExtractTextPlugin.extract({
            fallback: 'style-loader',
            use: _.compact([
              {
                loader: 'css-loader',
                options: {
                  minimize: prod,
                  importLoaders: 1,
                }
              },
              draft ? undefined : 'csso-loader',
              postCssLoader
            ])
          })
        },

        /* Bundles bootstrap css into the same bundle as the other css.
         * TODO look at running through csso, same as other css
         */
        {
          test: /\.less$/,
          exclude: /node_modules/,
          use: ExtractTextPlugin.extract({
            fallback: 'style-loader',
            use: [
              {
                loader: 'postcss-loader',
                options: {
                  plugins: [
                    require('stylelint'),
                  ]
                }
              },
              postCssLoader,
              'less-loader'
            ]
          })
        }
      ])
    },

    plugins: _.compact([
      /* Outputs css to a separate file per entry-point.
         Note the call to .extract above */
      new ExtractTextPlugin({
        filename: '[name].css',
        // storybook should use the fallback: style-loader
        disable: storybook
      }),
      new webpack.NoEmitOnErrorsPlugin(),

      prod
        ? new webpack.optimize.UglifyJsPlugin({ sourceMap: true })
        : undefined,
      prod
        // Workaround to switch old loaders to minimize mode
        ? new webpack.LoaderOptionsPlugin({ minimize: true })
        : undefined,

      new webpack.DefinePlugin({
        'process.env': {
          'NODE_ENV': JSON.stringify(
            dev || storybook ? 'development' : 'production')
        }
      }),

      // Prepare source strings for translation.
      // This puts the strings generated by react-intl into a single file.
      fullBuild
        ? new ReactIntlAggregatePlugin({
          messagesPattern: join(__dirname, 'build/messages/**/*.json'),
          aggregateOutputDir: join(__dirname, 'messages'),
          aggregateFilename: 'en-us'
        })
        : undefined,

      // Convert source (en-US) and translated strings to the format the app
      // can consume, in the dist directory.
      fullBuild
        ? new ReactIntlFlattenPlugin({
          aggregatePattern: join(__dirname, 'messages/*.json'),
          // dist/messages/[locale-id].json
          langOutputDir: 'messages'
        })
        : undefined
    ]),

    resolve: {
      /* Subdirectories to check while searching up tree for module
       * Default is ['', '.js'] */
      extensions: ['.js', '.jsx', '.json', '.css', '.less']
    },

    node: {
      __dirname: true
    },

    /* Caching for incremental builds, only needed for dev mode */
    cache: !fullBuild,

    /* fail on first error */
    bail: fullBuild,

    devtool: prod ? 'source-map' : 'eval'
  })
}
<|endoftext|>