<commit_before>

function runView(){

    function createTable(){
        var tableHtml = `

             Date
             Player
             Character
             Player
             Character

`;
        $('#output').html(tableHtml);
        $('.table-video').each(function (){
            $(this).DataTable({
                "fixedHeader": true,
                "paging": false,
                "ordering": true,
                "info": false,
                "bFilter": false,
                "columnDefs": [
                    // { "orderable": false, "targets": 0 }
                ],
                "order": [[0, 'desc']],
            });
        });
    }

    function setUrlParams(game, player, char1, char2){
        var params = "?game=" + game;
        if (player){
            params += "&player=" + player;
        }
        if (char1){
            params += "&char=" + char1;
        }
        if (char2){
            params += "&char=" + char2;
        }
        window.history.pushState({}, "", params);
    }

    function readUrlParams(manager){
        var game = TOOL.readUrlParam("game");
        if (game && manager.hasGame(game)){
            $('#game').val(game).prop('selected', true);
            var player = TOOL.readUrlParam("player");
            var chars = TOOL.readUrlParam("char", true);

            printResults(manager);
            if (player && manager.hasPlayer(game, player)){
                $('#player').val(player).prop('selected', true);
            }
            if (chars){
                var charId = 1;
                chars.forEach(function (char){
                    if (char && manager.hasCharacter(game, char)){
                        $('#char' + charId).val(char).prop('selected', true);
                        charId += 1;
                    }
                });
            }
        }
        printResults(manager);
    }

    function printResults(manager){
        var game = $('#game').val();

        if (manager.currentGame != game){
            manager.currentGame = game;
            var base_select = '<option value="">-</option>';
            var html_char1 = base_select;
            var html_char2 = base_select;
            var html_player = base_select;
            manager.getCharacters(game).forEach(function (char){
                html_char1 += TOOL.option(char);
                html_char2 += TOOL.option(char);
            });
            manager.getPlayers(game).forEach(function (player){
                html_player += TOOL.option(player);
            });
            $('#char1').html(html_char1);
            $('#char2').html(html_char2);
            $('#player').html(html_player);
        }

        var player = $('#player').val();
        var char1 = $('#char1').val();
        var char2 = $('#char2').val();
        setUrlParams(game, player, char1, char2);

        var videos = manager.getVideos(game, player, char1, char2);
        var out = $('#videos').DataTable()
        out.clear();
        videos.forEach(function (video){
            out.row.add(video.toData());
        });
        out.draw();
        $('.link-player').click(function (){
            var playerValue = $(this).data("value");
            $('#player').val(playerValue).prop('selected', true);
            $('#char1').val('').prop('selected', true);
            $('#char2').val('').prop('selected', true);
            $('#player').trigger('change');
        });
        $('.link-character').click(function (){
            var characterValue = $(this).data("value");
            if ($('#char1').val()){
                $('#char2').val(characterValue).prop('selected', true);
                $('#char2').trigger('change');
            } else {
                $('#char1').val(characterValue).prop('selected', true);
                $('#char1').trigger('change');
            }
        });
    }

    function onLoad(parsedVideos){
        $('#loading').hide();
        createTable();
        var manager = Manager();
        parsedVideos.forEach(function (video){
            manager.manageVideo(video);
        });

        // setup display
        var html_game = "";
        manager.getGames().forEach(function (game){
            html_game += '<option value="' + game + '">' + game + '</option>';
        });
        $('#game').html(html_game);
        $('#game').val('SF5').prop('selected', true);

        // setup triggers
        $('.filter').change(function (){
            printResults(manager);
        });
        $('.reset').click(function (){
            var select_id = $(this).data('id');
            $('#' + select_id).val('').prop('selected', true);
            $('#' + select_id).trigger('change');
        });

        readUrlParams(manager);
    }

    function setup(){
        if (TOOL.is_local){
            $(".container").append('

');
        }
        $('#enableJs').hide();
        var store = Store();
        store.load(onLoad);
    }

    setup();
}
<commit_msg>fix bug where a url with params would blink them out while loading<commit_after>

function runView(){

    function createTable(){
        var tableHtml = `

             Date
             Player
             Character
             Player
             Character

`;
        $('#output').html(tableHtml);
        $('.table-video').each(function (){
            $(this).DataTable({
                "fixedHeader": true,
                "paging": false,
                "ordering": true,
                "info": false,
                "bFilter": false,
                "columnDefs": [
                    // { "orderable": false, "targets": 0 }
                ],
                "order": [[0, 'desc']],
            });
        });
    }

    function setUrlParams(game, player, char1, char2){
        var params = "?game=" + game;
        if (player){
            params += "&player=" + player;
        }
        if (char1){
            params += "&char=" + char1;
        }
        if (char2){
            params += "&char=" + char2;
        }
        window.history.pushState({}, "", params);
    }

    function readUrlParams(manager){
        var game = TOOL.readUrlParam("game");
        if (game && manager.hasGame(game)){
            $('#game').val(game).prop('selected', true);
            var player = TOOL.readUrlParam("player");
            var chars = TOOL.readUrlParam("char", true);

            printResults(manager, true);
            if (player && manager.hasPlayer(game, player)){
                $('#player').val(player).prop('selected', true);
            }
            if (chars){
                var charId = 1;
                chars.forEach(function (char){
                    if (char && manager.hasCharacter(game, char)){
                        $('#char' + charId).val(char).prop('selected', true);
                        charId += 1;
                    }
                });
            }
        }
        printResults(manager);
    }

    function printResults(manager, dontSetUrlParams){
        var game = $('#game').val();

        if (manager.currentGame != game){
            manager.currentGame = game;
            var base_select = '<option value="">-</option>';
            var html_char1 = base_select;
            var html_char2 = base_select;
            var html_player = base_select;
            manager.getCharacters(game).forEach(function (char){
                html_char1 += TOOL.option(char);
                html_char2 += TOOL.option(char);
            });
            manager.getPlayers(game).forEach(function (player){
                html_player += TOOL.option(player);
            });
            $('#char1').html(html_char1);
            $('#char2').html(html_char2);
            $('#player').html(html_player);
        }

        var player = $('#player').val();
        var char1 = $('#char1').val();
        var char2 = $('#char2').val();
        if (!dontSetUrlParams){
            setUrlParams(game, player, char1, char2);
        }

        var videos = manager.getVideos(game, player, char1, char2);
        var out = $('#videos').DataTable()
        out.clear();
        videos.forEach(function (video){
            out.row.add(video.toData());
        });
        out.draw();
        $('.link-player').click(function (){
            var playerValue = $(this).data("value");
            $('#player').val(playerValue).prop('selected', true);
            $('#char1').val('').prop('selected', true);
            $('#char2').val('').prop('selected', true);
            $('#player').trigger('change');
        });
        $('.link-character').click(function (){
            var characterValue = $(this).data("value");
            if ($('#char1').val()){
                $('#char2').val(characterValue).prop('selected', true);
                $('#char2').trigger('change');
            } else {
                $('#char1').val(characterValue).prop('selected', true);
                $('#char1').trigger('change');
            }
        });
    }

    function onLoad(parsedVideos){
        $('#loading').hide();
        createTable();
        var manager = Manager();
        parsedVideos.forEach(function (video){
            manager.manageVideo(video);
        });

        // setup display
        var html_game = "";
        manager.getGames().forEach(function (game){
            html_game += '<option value="' + game + '">' + game + '</option>';
        });
        $('#game').html(html_game);
        $('#game').val('SF5').prop('selected', true);

        // setup triggers
        $('.filter').change(function (){
            printResults(manager);
        });
        $('.reset').click(function (){
            var select_id = $(this).data('id');
            $('#' + select_id).val('').prop('selected', true);
            $('#' + select_id).trigger('change');
        });

        readUrlParams(manager);
    }

    function setup(){
        if (TOOL.is_local){
            $(".container").append('

');
        }
        $('#enableJs').hide();
        var store = Store();
        store.load(onLoad);
    }

    setup();
}
<|endoftext|>


<commit_before>// ---------------------------------------------------
//  Worm
// ---------------------------------------------------
var Worm = function(s, p, c) {

  var r1 = Math.min((s.w / 4), 200);
  var r2 = Math.min((s.w / 8), 200);

  this.pathBody = buildBody(r1, s.h);
  // Eyes
  this.pathLeftEye = buildEye(r2, [r1/3, s.h/1.75]);
  this.pathRightEye = buildEye(r2, [5*r1/3, s.h/1.75]);
  // Pupils
  this.pathLeftPupil = buildPupil(r2/6, [r1/3, s.h/1.6]);
  this.pathRightPupil = buildPupil(r2/6, [5*r1/3, s.h/1.6]);

  this.all = new Group({
    children: [
      this.pathBody,
      this.pathLeftEye,
      this.pathRightEye,
      this.pathLeftPupil,
      this.pathRightPupil
    ]
  });

  this.all.position.x = paper.view.center.x;

};

Worm.prototype.resize = function() {

};

// Build the body path
var buildBody = function(r, h) {

  var path = new Path({
    fillColor: colors.green
  });

  path.segments = [
    [0, h],
    [[0, h/2], null, [0, -r*KAPPA]],
    [[r, h/2 - r], [-r*KAPPA, 0], [r*KAPPA, 0 ]],
    [[2*r, h/2], [0, -r*KAPPA], null],
    [2*r, h]
  ];

  if (debug) path.fullySelected = true;

  var l = path.length;
  var p1 = path.getPointAt(0.6 * l);

  // path.insertSegments(3, [p1]);
  path.flatten(25);
  path.smooth();

  return path;

};

// Build the eye path
var buildEye = function(r, p) {

  var path = new Path.Circle({
    fillColor: colors.white,
    strokeColor: colors.grey,
    radius: r,
    position: p
  });

  if (debug) path.fullySelected = true;

  return path;

};

// Build the pupil path
var buildPupil = function(r, p) {

  var path = new Path.Circle({
    fillColor: colors.black,
    radius: r,
    position: p
  });

  if (debug) path.fullySelected = true;

  return path;

};<commit_msg>cleanup<commit_after>// ---------------------------------------------------
//  Worm
// ---------------------------------------------------
var Worm = function(s, p, c) {

  var r1 = Math.min((s.w / 4), 200);
  var r2 = Math.min((s.w / 8), 200);

  this.pathBody = buildBody(r1, s.h);
  // Eyes
  this.pathLeftEye = buildEye(r2, [r1/3, s.h/1.75]);
  this.pathRightEye = buildEye(r2, [5*r1/3, s.h/1.75]);
  // Pupils
  this.pathLeftPupil = buildPupil(r2/6, [r1/3, s.h/1.6]);
  this.pathRightPupil = buildPupil(r2/6, [5*r1/3, s.h/1.6]);

  this.all = new Group({
    children: [
      this.pathBody,
      this.pathLeftEye,
      this.pathRightEye,
      this.pathLeftPupil,
      this.pathRightPupil
    ]
  });

  this.all.position.x = paper.view.center.x;

};

Worm.prototype.resize = function() {

};

// Build the body path
var buildBody = function(r, h) {

  var path = new Path({
    fillColor: colors.green
  });

  path.segments = [
    [0, h],
    [[0, h/2], null, [0, -r*KAPPA]],
    [[r, h/2 - r], [-r*KAPPA, 0], [r*KAPPA, 0 ]],
    [[2*r, h/2], [0, -r*KAPPA], null],
    [2*r, h]
  ];

  if (debug) path.fullySelected = true;

  path.flatten(25);
  path.smooth();

  return path;

};

// Build the eye path
var buildEye = function(r, p) {

  var path = new Path.Circle({
    fillColor: colors.white,
    strokeColor: colors.grey,
    radius: r,
    position: p
  });

  if (debug) path.fullySelected = true;

  return path;

};

// Build the pupil path
var buildPupil = function(r, p) {

  var path = new Path.Circle({
    fillColor: colors.black,
    radius: r,
    position: p
  });

  if (debug) path.fullySelected = true;

  return path;

};<|endoftext|>


<commit_before>var path = require('path');

function clean(node) {
    delete node.source.input.css;
    node.source.input.file = path.basename(node.source.input.file);
    node.source.input.from = path.basename(node.source.input.from);
    if ( node.nodes ) node.nodes = node.nodes.map(clean);
    return node;
}

module.exports = function jsonify(node) {
    var cleaned = clean(node.toJSON());
    return JSON.stringify(cleaned, null, 4);
};
<commit_msg>Remove unnecessary from cleaning from jsonify<commit_after>var path = require('path');

function clean(node) {
    delete node.source.input.css;
    node.source.input.file = path.basename(node.source.input.file);
    if ( node.nodes ) node.nodes = node.nodes.map(clean);
    return node;
}

module.exports = function jsonify(node) {
    var cleaned = clean(node.toJSON());
    return JSON.stringify(cleaned, null, 4);
};
<|endoftext|>


<commit_before>var EXCEPTIONS = [
    "abp",
    "account",
    "auth",
    "download",
    "login",
    "logout",
    "register",
    "signin",
    "signoff",
    "signon",
    "signout",
    "signup",
    "subscribe",
];

var PROTOCOLS = [
    "ftp",
    "http",
    "https",
    "mms",
    "rtsp",
];

var protocolsGroup = "(?:" + PROTOCOLS.join("|") + ")";
var colonGroup = "(:|%3A|%253A)";
var protocol = protocolsGroup + colonGroup;

var simpleRegexpProtocol     = new RegExp("https?://.*" + "(?:\\?|/)" + "(" + protocol + ".*$" + ")");
var simpleRegexpWWW          = new RegExp("https?://.*" + "(?:\\?|/)" + "(" + "www\\." + ".*$" + ")");
var queryStringRegexProtocol = new RegExp("https?://.*" +         "=" + "(" + protocol + "[^?&;#]*" + ")");
var queryStringRegexWWW      = new RegExp("https?://.*" +         "=" + "(" + "www\\." + "[^?&;#]*" + ")");

function maybeDecode(urlMatch, colonMatch) {
    switch(colonMatch) {
        case "%253A":
            return decodeURIComponent(decodeURIComponent(urlMatch));
        case "%3A":
            return decodeURIComponent(urlMatch);
        case ":":
            return urlMatch;
        case undefined:
            return "http://" + maybeDecodeWWWMatch(urlMatch);
    }
}

function maybeDecodeWWWMatch(wwwMatch) {
    // %
    if (new RegExp("%25").test(wwwMatch)) {
        wwwMatch = decodeURIComponent(wwwMatch);
    }
    // #$&+,/:;=?@
    if (new RegExp("(%23|%24|%26|%2B|%2C|%2F|%3A|%3B|%3D|%3F|%40)").test(wwwMatch)) {
        wwwMatch = decodeURIComponent(wwwMatch);
    }
    return wwwMatch;
}

function getRedirectTarget(url) {
    if (new RegExp("(" + EXCEPTIONS.join("|") + ")").test(url)) {
        return undefined;
    }

    let matches =
        url.match(simpleRegexpProtocol, "i") ||
        url.match(simpleRegexpWWW, "i") ||
        url.match(queryStringRegexProtocol, "i") ||
        url.match(queryStringRegexWWW, "i") ||
        undefined;
    if (matches) {
        let [urlMatch, colonMatch] = matches.splice(1, 2);
        return maybeDecode(urlMatch, colonMatch);
    }
    return undefined;
}

exports.getRedirectTarget = getRedirectTarget;
<commit_msg>Rename regexp<commit_after>var EXCEPTIONS = [
    "abp",
    "account",
    "auth",
    "download",
    "login",
    "logout",
    "register",
    "signin",
    "signoff",
    "signon",
    "signout",
    "signup",
    "subscribe",
];

var PROTOCOLS = [
    "ftp",
    "http",
    "https",
    "mms",
    "rtsp",
];

var protocolsGroup = "(?:" + PROTOCOLS.join("|") + ")";
var colonGroup = "(:|%3A|%253A)";
var protocol = protocolsGroup + colonGroup;

var pathRegexpProtocol       = new RegExp("https?://.*" + "(?:\\?|/)" + "(" + protocol + ".*$" + ")");
var pathRegexpWWW            = new RegExp("https?://.*" + "(?:\\?|/)" + "(" + "www\\." + ".*$" + ")");
var queryStringRegexProtocol = new RegExp("https?://.*" +         "=" + "(" + protocol + "[^?&;#]*" + ")");
var queryStringRegexWWW      = new RegExp("https?://.*" +         "=" + "(" + "www\\." + "[^?&;#]*" + ")");

function maybeDecode(urlMatch, colonMatch) {
    switch(colonMatch) {
        case "%253A":
            return decodeURIComponent(decodeURIComponent(urlMatch));
        case "%3A":
            return decodeURIComponent(urlMatch);
        case ":":
            return urlMatch;
        case undefined:
            return "http://" + maybeDecodeWWWMatch(urlMatch);
    }
}

function maybeDecodeWWWMatch(wwwMatch) {
    // %
    if (new RegExp("%25").test(wwwMatch)) {
        wwwMatch = decodeURIComponent(wwwMatch);
    }
    // #$&+,/:;=?@
    if (new RegExp("(%23|%24|%26|%2B|%2C|%2F|%3A|%3B|%3D|%3F|%40)").test(wwwMatch)) {
        wwwMatch = decodeURIComponent(wwwMatch);
    }
    return wwwMatch;
}

function getRedirectTarget(url) {
    if (new RegExp("(" + EXCEPTIONS.join("|") + ")").test(url)) {
        return undefined;
    }

    let matches =
        url.match(pathRegexpProtocol, "i") ||
        url.match(pathRegexpWWW, "i") ||
        url.match(queryStringRegexProtocol, "i") ||
        url.match(queryStringRegexWWW, "i") ||
        undefined;
    if (matches) {
        let [urlMatch, colonMatch] = matches.splice(1, 2);
        return maybeDecode(urlMatch, colonMatch);
    }
    return undefined;
}

exports.getRedirectTarget = getRedirectTarget;
<|endoftext|>


<commit_before>var Int64 = require( 'int64-native' )

/**
 * VolumeBootRecord Constructor
 * @return {VolumeBootRecord}
 */
function VolumeBootRecord() {

  if( !(this instanceof VolumeBootRecord) )
    return new VolumeBootRecord()

  Buffer.call( this, 512 )

}

/**
 * VolumeBootRecord Prototype
 * @type {Object}
 */
VolumeBootRecord.prototype = {

  constructor: VolumeBootRecord,

  get jmp() {
    return this.slice( 0, 3 )
  },

  get OEMID() {
    return this.toString( 'ascii', 3, 11 )
  },

  get bpb() {
    return {
      bytesPerSector: this.readUInt16LE( 11 ),
      sectorsPerCluster: this.readUInt8( 13 ),
      reservedSectors: this.readUInt16LE( 14 ),
      numberOfFATs: this.readUInt8( 16 ),
      rootEntries: this.readUInt16LE( 17 ),
      numberOfSectors: this.readUInt16LE( 19 ),
      mediaType: this.readUInt8( 21 ),
      sectorsPerFAT: this.readUInt16LE( 22 ),
      sectorsPerTrack: this.readUInt16LE( 24 ),
      headsPerTrack: this.readUInt16LE( 26 ),
      hiddenSectors: this.readUInt32LE( 28 ),
      largeSectors: this.readUInt32LE( 32 ),
    }
  },

  get physicalDrive() {
    return this.readUInt8( 36 )
  },

  get currentHead() {
    return this.readUInt8( 37 )
  },

  get extendedBootSignature() {
    return this.readUInt8( 38 )
  },

  get numberOfSectors() {
    return +(new Int64(
      this.readUInt32BE( 40 ),
      this.readUInt32BE( 44 )
    ).toUnsignedDecimalString())
  },

  get mftLocation() {
    return +(new Int64(
      this.readUInt32BE( 48 ),
      this.readUInt32BE( 52 )
    ).toUnsignedDecimalString())
  },

  get mftMirror() {
    return +(new Int64(
      this.readUInt32BE( 56 ),
      this.readUInt32BE( 60 )
    ).toUnsignedDecimalString())
  },

  get clustersPerMFTRecord() {
    return this.readUInt8( 64 )
  },

  // reserved [u8][3]

  get clustersPerIndexRecord() {
    return this.readUInt8( 68 )
  },

  // reserved [u8][3]

  get serialNumber() {
    return this.slice( 72, 80 )
  },

  get checksum() {
    return this.readUInt32LE( 80 )
  },

  get bootCode() {
    return this.slice( 84, 84 + 426 )
  },

  get endOfSectorMarker() {
    return this.readUInt16LE( 510 )
  },

  valueOf: function() {
    return {
      jmp: this.jmp,
      OEMID: this.OEMID,
      bpb: this.bpb,
      physicalDrive: this.physicalDrive,
      currentHead: this.currentHead,
      extendedBootSignature: this.extendedBootSignature,
      numberOfSectors: this.numberOfSectors,
      mftLocation: this.mftLocation,
      mftMirror: this.mftMirror,
      clustersPerMFTRecord: this.clustersPerMFTRecord,
      serialNumber: this.serialNumber,
      checksum: this.checksum,
      bootCode: this.bootCode,
      endOfSectorMarker: this.endOfSectorMarker,
    }
  },

  inspect: function() {
    return '<Buffer ' +
      require( 'util' ).inspect( this.valueOf(), {
        colors: true,
        depth: null
      }) + '>'
  },

}

// Inherit from Buffer
VolumeBootRecord.prototype.__proto__ =
  Buffer.prototype

// Exports
module.exports = VolumeBootRecord
<commit_msg>Updated lib/vbr: Zeroing buffer in ctor<commit_after>var Int64 = require( 'int64-native' )

/**
 * VolumeBootRecord Constructor
 * @return {VolumeBootRecord}
 */
function VolumeBootRecord() {

  if( !(this instanceof VolumeBootRecord) )
    return new VolumeBootRecord()

  // Inherit from Buffer
  // and init sizeof( 512 )
  Buffer.call( this, 512 )
  // Zero it out
  this.fill( 0 )

}

/**
 * VolumeBootRecord Prototype
 * @type {Object}
 */
VolumeBootRecord.prototype = {

  constructor: VolumeBootRecord,

  get jmp() {
    return this.slice( 0, 3 )
  },

  get OEMID() {
    return this.toString( 'ascii', 3, 11 )
  },

  get bpb() {
    return {
      bytesPerSector: this.readUInt16LE( 11 ),
      sectorsPerCluster: this.readUInt8( 13 ),
      reservedSectors: this.readUInt16LE( 14 ),
      numberOfFATs: this.readUInt8( 16 ),
      rootEntries: this.readUInt16LE( 17 ),
      numberOfSectors: this.readUInt16LE( 19 ),
      mediaType: this.readUInt8( 21 ),
      sectorsPerFAT: this.readUInt16LE( 22 ),
      sectorsPerTrack: this.readUInt16LE( 24 ),
      headsPerTrack: this.readUInt16LE( 26 ),
      hiddenSectors: this.readUInt32LE( 28 ),
      largeSectors: this.readUInt32LE( 32 ),
    }
  },

  get physicalDrive() {
    return this.readUInt8( 36 )
  },

  get currentHead() {
    return this.readUInt8( 37 )
  },

  get extendedBootSignature() {
    return this.readUInt8( 38 )
  },

  get numberOfSectors() {
    return +(new Int64(
      this.readUInt32BE( 40 ),
      this.readUInt32BE( 44 )
    ).toUnsignedDecimalString())
  },

  get mftLocation() {
    return +(new Int64(
      this.readUInt32BE( 48 ),
      this.readUInt32BE( 52 )
    ).toUnsignedDecimalString())
  },

  get mftMirror() {
    return +(new Int64(
      this.readUInt32BE( 56 ),
      this.readUInt32BE( 60 )
    ).toUnsignedDecimalString())
  },

  get clustersPerMFTRecord() {
    return this.readUInt8( 64 )
  },

  // reserved [u8][3]

  get clustersPerIndexRecord() {
    return this.readUInt8( 68 )
  },

  // reserved [u8][3]

  get serialNumber() {
    return this.slice( 72, 80 )
  },

  get checksum() {
    return this.readUInt32LE( 80 )
  },

  get bootCode() {
    return this.slice( 84, 84 + 426 )
  },

  get endOfSectorMarker() {
    return this.readUInt16LE( 510 )
  },

  valueOf: function() {
    return {
      jmp: this.jmp,
      OEMID: this.OEMID,
      bpb: this.bpb,
      physicalDrive: this.physicalDrive,
      currentHead: this.currentHead,
      extendedBootSignature: this.extendedBootSignature,
      numberOfSectors: this.numberOfSectors,
      mftLocation: this.mftLocation,
      mftMirror: this.mftMirror,
      clustersPerMFTRecord: this.clustersPerMFTRecord,
      serialNumber: this.serialNumber,
      checksum: this.checksum,
      bootCode: this.bootCode,
      endOfSectorMarker: this.endOfSectorMarker,
    }
  },

  inspect: function() {
    return '<Buffer ' +
      require( 'util' ).inspect( this.valueOf(), {
        colors: true,
        depth: null
      }) + '>'
  },

}

// Inherit from Buffer
VolumeBootRecord.prototype.__proto__ =
  Buffer.prototype

// Exports
module.exports = VolumeBootRecord
<|endoftext|>


<commit_before>var fs = require("fs");

module.exports = function vfs(request) {
    return {
        getFile: function getFile(path, cb) {
            request("/api/vfs/" + path, function(err, response, body) {
                if (err) return cb(err);
                cb(null, body, response);
            });
        },
        listFiles: function listFiles(path, cb) {
            if ('/' !== path[path.length-1]) {
                path += '/';
            }
            request("/api/vfs/" + path, function(err, response, body) {
                if (err) return cb(err);
                cb(null, JSON.parse(body), response);
            });
        },
        uploadFile: function uploadFile(localPath, destPath, etag, cb) {
            if (typeof etag === "function") {
                cb = etag;
                etag = "*";
            }
            fs.createReadStream(localPath).pipe(request.put({
                uri: "/api/vfs/" + destPath,
                headers: {
                    "If-Match": etag
                }
            }, function(err, response, body) {
                if (err) return cb(err);

                if (response.statusCode !== 204) {
                    cb(JSON.parse(body), response);
                }

                cb(null, response);
            }));
        },
        createDirectory: function createDirectory(path, cb) {
            if ('/' !== path[path.length-1]) {
                path += '/';
            }
            request.put("/api/vfs/" + path, function(err, response, body) {
                if (err) return cb(err);
                cb(null, body, response);
            });
        },
        deleteFile: function deleteFile(path, cb) {
            request.del("/api/vfs/" + path, function(err, response, body) {
                if (err) return cb(err);
                cb(null, body, response);
            });
        },
        deleteDirectory: function deleteDirectory(path, cb) {
            if ('/' !== path[path.length-1]) {
                path += '/';
            }
            request.del("/api/vfs/" + path, function(err, response, body) {
                if (err) return cb(err);
                cb(null, body, response);
            });
        }
    }
};
<commit_msg>Only parse upload file body for HTTP error codes.<commit_after>var fs = require("fs");

module.exports = function vfs(request) {
    return {
        getFile: function getFile(path, cb) {
            request("/api/vfs/" + path, function(err, response, body) {
                if (err) return cb(err);
                cb(null, body, response);
            });
        },
        listFiles: function listFiles(path, cb) {
            if ('/' !== path[path.length-1]) {
                path += '/';
            }
            request("/api/vfs/" + path, function(err, response, body) {
                if (err) return cb(err);
                cb(null, JSON.parse(body), response);
            });
        },
        uploadFile: function uploadFile(localPath, destPath, etag, cb) {
            if (typeof etag === "function") {
                cb = etag;
                etag = "*";
            }
            fs.createReadStream(localPath).pipe(request.put({
                uri: "/api/vfs/" + destPath,
                headers: {
                    "If-Match": etag
                }
            }, function(err, response, body) {
                if (err) return cb(err);

                if (response.statusCode >= 400) {
                    cb(JSON.parse(body), response);
                }

                cb(null, response);
            }));
        },
        createDirectory: function createDirectory(path, cb) {
            if ('/' !== path[path.length-1]) {
                path += '/';
            }
            request.put("/api/vfs/" + path, function(err, response, body) {
                if (err) return cb(err);
                cb(null, body, response);
            });
        },
        deleteFile: function deleteFile(path, cb) {
            request.del("/api/vfs/" + path, function(err, response, body) {
                if (err) return cb(err);
                cb(null, body, response);
            });
        },
        deleteDirectory: function deleteDirectory(path, cb) {
            if ('/' !== path[path.length-1]) {
                path += '/';
            }
            request.del("/api/vfs/" + path, function(err, response, body) {
                if (err) return cb(err);
                cb(null, body, response);
            });
        }
    }
};
<|endoftext|>


<commit_before>const debug = require('debug')('weh')
const trough = require('trough')
const read = require('./read')
const write = require('./write')
const { generateConfig } = require('./config')

async function siteUse (fn, opts = {}) {
  const plugin = await fn(opts)
  this.hooks.use(plugin)
}

function siteConfig (conf = {}) {
  this._config = generateConfig(conf)
}

function _runMiddleware (hooks, ...args) {
  return new Promise((resolve, reject) => {
    hooks.run(...args, (err, res) => {
      if (err) reject(err)
      resolve(res)
    })
  })
}

async function buildSite (site) {
  site.files = []

  debug('starting read...')
  try {
    site.files = site._config.dry_run ? site._config.files : await read(site._config)
  } catch (errs) {
    debug('one or more errors occured while reading:')
    errs.forEach(err => { throw err })
  }

  debug('running plugins...')
  await _runMiddleware(site.hooks, site.files)

  debug('got %d files', site.files.length)
  if (site._config.dry_run) {
    debug('skipping write...')
  } else {
    debug('starting write...')
    site = await write(site)
  }

  debug('all done!')
  return site
}

async function wehCore (site, fn) {
  site.config = siteConfig.bind(site)
  site.use = siteUse.bind(site)

  debug('weh is ready to go!!!')

  const resSite = await fn(site)
  if (Object.keys(site._config).length === 0) {
    site._config = generateConfig({})
  }
  return buildSite(resSite)
}

async function weh (fn) {
  const site = {}
  site._config = {}
  site.hooks = trough()
  return wehCore(site, fn)
}

async function wehIntegration (fn, files) {
  const site = {}
  site._config = {
    dry_run: true,
    files
  }
  site.hooks = trough()
  return wehCore(site, fn)
}

module.exports = weh
module.exports.integration = wehIntegration
<commit_msg>fix bug where you couldn't add files<commit_after>const debug = require('debug')('weh')
const trough = require('trough')
const read = require('./read')
const write = require('./write')
const { generateConfig } = require('./config')

async function siteUse (fn, opts = {}) {
  const plugin = await fn(opts)
  this.hooks.use(plugin)
}

function siteConfig (conf = {}) {
  this._config = generateConfig(conf)
}

function _runMiddleware (hooks, files) {
  return new Promise((resolve, reject) => {
    hooks.run(files, (err, res) => {
      if (err) reject(err)
      resolve(res)
    })
  })
}

async function buildSite (site) {
  site.files = []

  debug('starting read...')
  try {
    site.files = site._config.dry_run ? site._config.files : await read(site._config)
  } catch (errs) {
    debug('one or more errors occured while reading:')
    errs.forEach(err => { throw err })
  }

  debug('running plugins...')
  site.files = await _runMiddleware(site.hooks, site.files)

  debug('got %d files', site.files.length)
  if (site._config.dry_run) {
    debug('skipping write...')
  } else {
    debug('starting write...')
    site = await write(site)
  }

  debug('all done!')
  return site
}

async function wehCore (site, fn) {
  site.config = siteConfig.bind(site)
  site.use = siteUse.bind(site)

  debug('weh is ready to go!!!')

  const resSite = await fn(site)
  if (Object.keys(site._config).length === 0) {
    site._config = generateConfig({})
  }
  return buildSite(resSite)
}

async function weh (fn) {
  const site = {}
  site._config = {}
  site.hooks = trough()
  return wehCore(site, fn)
}

async function wehIntegration (fn, files) {
  const site = {}
  site._config = {
    dry_run: true,
    files
  }
  site.hooks = trough()
  return wehCore(site, fn)
}

module.exports = weh
module.exports.integration = wehIntegration
<|endoftext|>


<commit_before>var url = require('url')
  , stream = require('stream')
  , rest = require('./rest');

function WXS(properties) {
  this.wxs = properties;
  if (this.wxs.restResourceSecure.indexOf('https://') != 0)
    this.wxs.restResourceSecure = 'https://' + this.wxs.restResourceSecure;
  this.auth = 'Basic '
      + new Buffer(this.wxs.username + ':' + this.wxs.password)
          .toString('base64');
  var p = url.parse(this.wxs.restResourceSecure);
  this.baseUrl = 'https://' + p.hostname;
  this.pathname = p.pathname;
}

WXS.prototype = {

  /**
   * Cache a key/vale pair in a map.
   *
   * @param map           the map name
   * @param key           the name of the key
   * @param value         the value, which may represent an xml, json, or anything else, depending on contenttype
   *                      the value should be a string, an object, or a stream.Readable. If it is an object the
   *                      the JSON.stringify() will be applied before sending out.
   * @param contentType   the type of the value application/xml, application/json, application/octet-stream
   * @param callback(err) the function handling the result of the post. in case of failure
   *                      result is an Error object that may contain the fields
   *                      status: <response status code>, message: <data sent with the
   *                      response>, responseHeaders: <the headers in the response>.
   *                      in case of success nothing is passed to the callback.
   */
  put : function(map, key, value, contentType, size, callback) {

    switch (contentType.toLocaleLowerCase()) {
      case 'application/json':
      case 'application/xml':
      case 'application/octet-stream':
        break;
      default:
        contentType = 'application/octet-stream';
    }

    // If map not set, then use the dafault map, which has the name of the grid
    if ( !map || map.trim().length==0 )
      map = this.wxs.gridName;

    var resource = this.pathname + '/' + encodeURIComponent(map) + '/' + encodeURIComponent(key);
    var headers = {
      'Content-Type' : contentType,
      'Authorization' : this.auth
    };
    var options = {
      rejectUnauthorized : false,
      agent : false
    };

    if ( typeof value === "object" && !(value instanceof stream.Readable) ) {
      value = JSON.stringify(value);
    }

    if ( value instanceof stream.Readable ) {
      if ( size && typeof size === "number" ) {
        headers['Content-Length'] = size;
      } else {
        headers['Transfer-Encoding'] = 'chunked';
      }
    }

    if ( typeof size === "function" ) {
      callback = size;
    }

    rest.post(this.baseUrl, resource, headers, options, value, function(res) {
        callback && res instanceof Error ? callback(res): callback();
      });
  },

  /**
   * Get the value of a key in a map.
   *
   * @param map           the map name
   * @param key           the name of the key
   * @param callback(val) the function handling the result of the get. in case of failure
   *                      result is an Error object that may contain the fields
   *                      status: <response status code>, message: <data sent with the
   *                      response>, responseHeaders: <the headers in the response>.
   *                      in case of success val is an object with:
   *                      status: <response status code>, responseText: <value of key in map>,
   *                      responseHeaders: <the headers in the response>. You can read
   *                      the type of the value from the content-type header of the response.
   */
  get : function(map, key, callback) {

    // If map not set, then use the dafault map, which has the name of the grid
    if ( !map || map.trim().length==0 )
      map = this.wxs.gridName;

    var resource = this.pathname + '/' + encodeURIComponent(map) + '/' + encodeURIComponent(key);
    var headers = {
      'Accept' : '*/*',
      'Authorization' : this.auth
    };
    var options = {
      rejectUnauthorized : false,
      agent : false
    };

    rest.get(this.baseUrl, resource, headers, options, function(res) {
        callback && callback(res);
      });
  },

  /**
   * Remove a key from a map.
   *
   * @param map           the map name
   * @param key           the name of the key
   * @param callback(res) the function handling the result of the get. in case of failure
   *                      result is an Error object that may contain the fields
   *                      status: <response status code>, message: <data sent with the
   *                      response>, responseHeaders: <the headers in the response>.
   *                      Nothing is passed to callback in case of success.
   */
  remove : function(map, key, callback) {

    // If map not set, then use the dafault map, which has the name of the grid
    if ( !map || map.trim().length==0 )
      map = this.wxs.gridName;

    var resource = this.pathname + '/' + encodeURIComponent(map) + '/' + encodeURIComponent(key);
    var headers = {
      'Accept' : '*/*',
      'Authorization' : this.auth
    };
    var options = {
      rejectUnauthorized : false,
      agent : false
    };

    rest.del(this.baseUrl, resource, headers, options, function(res) {
        callback && res instanceof Error ? callback(res): callback();
      });
  },

  /**
   * Remove an entire map from the cache.
   *
   * @param map           the map name
   * @param callback(res) the function handling the result of the get. in case of failure
   *                      result is an Error object that may contain the fields
   *                      status: <response status code>, message: <data sent with the
   *                      response>, responseHeaders: <the headers in the response>.
   *                      Nothing is passed to callback in case of success.
   */
  clearMap : function(map, callback) {

    // If map not set, then use the dafault map, which has the name of the grid
    if ( !map || map.trim().length==0 )
      map = this.wxs.gridName;

    var resource = this.pathname + '/' + encodeURIComponent(map);
    var headers = {
      'Accept' : '*/*',
      'Authorization' : this.auth
    };
    var options = {
      rejectUnauthorized : false,
      agent : false
    };

    rest.del(this.baseUrl, resource, headers, options, function(res) {
        callback && res instanceof Error ? callback(res): callback();
      });
  }
};

module.exports = WXS;
<commit_msg>switching to non-secure urls...getting 500 Error<commit_after>var url = require('url')
  , stream = require('stream')
  , rest = require('./rest');

function WXS(properties) {
  this.wxs = properties;
  if (this.wxs.restResource.indexOf('http://') != 0)
    this.wxs.restResource = 'http://' + this.wxs.restResource;
  this.auth = 'Basic '
      + new Buffer(this.wxs.username + ':' + this.wxs.password)
          .toString('base64');
  var p = url.parse(this.wxs.restResource);
  this.baseUrl = 'http://' + p.hostname;
  this.pathname = p.pathname;
}

WXS.prototype = {

  /**
   * Cache a key/vale pair in a map.
   *
   * @param map           the map name
   * @param key           the name of the key
   * @param value         the value, which may represent an xml, json, or anything else, depending on contenttype
   *                      the value should be a string, an object, or a stream.Readable. If it is an object the
   *                      the JSON.stringify() will be applied before sending out.
   * @param contentType   the type of the value application/xml, application/json, application/octet-stream
   * @param callback(err) the function handling the result of the post. in case of failure
   *                      result is an Error object that may contain the fields
   *                      status: <response status code>, message: <data sent with the
   *                      response>, responseHeaders: <the headers in the response>.
   *                      in case of success nothing is passed to the callback.
   */
  put : function(map, key, value, contentType, size, callback) {

    switch (contentType.toLocaleLowerCase()) {
      case 'application/json':
      case 'application/xml':
      case 'application/octet-stream':
        break;
      default:
        contentType = 'application/octet-stream';
    }

    // If map not set, then use the dafault map, which has the name of the grid
    if ( !map || map.trim().length==0 )
      map = this.wxs.gridName;

    var resource = this.pathname + '/' + encodeURIComponent(map) + '/' + encodeURIComponent(key);
    var headers = {
      'Content-Type' : contentType,
      'Authorization' : this.auth
    };
    var options = {
      rejectUnauthorized : false,
      agent : false
    };

    if ( typeof value === "object" && !(value instanceof stream.Readable) ) {
      value = JSON.stringify(value);
    }

    if ( value instanceof stream.Readable ) {
      if ( size && typeof size === "number" ) {
        headers['Content-Length'] = size;
      } else {
        headers['Transfer-Encoding'] = 'chunked';
      }
    }

    if ( typeof size === "function" ) {
      callback = size;
    }

    rest.post(this.baseUrl, resource, headers, options, value, function(res) {
        callback && res instanceof Error ? callback(res): callback();
      });
  },

  /**
   * Get the value of a key in a map.
   *
   * @param map           the map name
   * @param key           the name of the key
   * @param callback(val) the function handling the result of the get. in case of failure
   *                      result is an Error object that may contain the fields
   *                      status: <response status code>, message: <data sent with the
   *                      response>, responseHeaders: <the headers in the response>.
   *                      in case of success val is an object with:
   *                      status: <response status code>, responseText: <value of key in map>,
   *                      responseHeaders: <the headers in the response>. You can read
   *                      the type of the value from the content-type header of the response.
   */
  get : function(map, key, callback) {

    // If map not set, then use the dafault map, which has the name of the grid
    if ( !map || map.trim().length==0 )
      map = this.wxs.gridName;

    var resource = this.pathname + '/' + encodeURIComponent(map) + '/' + encodeURIComponent(key);
    var headers = {
      'Accept' : '*/*',
      'Authorization' : this.auth
    };
    var options = {
      rejectUnauthorized : false,
      agent : false
    };

    rest.get(this.baseUrl, resource, headers, options, function(res) {
        callback && callback(res);
      });
  },

  /**
   * Remove a key from a map.
   *
   * @param map           the map name
   * @param key           the name of the key
   * @param callback(res) the function handling the result of the get. in case of failure
   *                      result is an Error object that may contain the fields
   *                      status: <response status code>, message: <data sent with the
   *                      response>, responseHeaders: <the headers in the response>.
   *                      Nothing is passed to callback in case of success.
   */
  remove : function(map, key, callback) {

    // If map not set, then use the dafault map, which has the name of the grid
    if ( !map || map.trim().length==0 )
      map = this.wxs.gridName;

    var resource = this.pathname + '/' + encodeURIComponent(map) + '/' + encodeURIComponent(key);
    var headers = {
      'Accept' : '*/*',
      'Authorization' : this.auth
    };
    var options = {
      rejectUnauthorized : false,
      agent : false
    };

    rest.del(this.baseUrl, resource, headers, options, function(res) {
        callback && res instanceof Error ? callback(res): callback();
      });
  },

  /**
   * Remove an entire map from the cache.
   *
   * @param map           the map name
   * @param callback(res) the function handling the result of the get. in case of failure
   *                      result is an Error object that may contain the fields
   *                      status: <response status code>, message: <data sent with the
   *                      response>, responseHeaders: <the headers in the response>.
   *                      Nothing is passed to callback in case of success.
   */
  clearMap : function(map, callback) {

    // If map not set, then use the dafault map, which has the name of the grid
    if ( !map || map.trim().length==0 )
      map = this.wxs.gridName;

    var resource = this.pathname + '/' + encodeURIComponent(map);
    var headers = {
      'Accept' : '*/*',
      'Authorization' : this.auth
    };
    var options = {
      rejectUnauthorized : false,
      agent : false
    };

    rest.del(this.baseUrl, resource, headers, options, function(res) {
        callback && res instanceof Error ? callback(res): callback();
      });
  }
};

module.exports = WXS;
<|endoftext|>


<commit_before>/**
 * Enhancements to adm-zip.
 *
 * @module xml
 *
 * @copyright
 * Copyright (c) 2009-2013 by Appcelerator, Inc. All Rights Reserved.
 *
 * @license
 * Licensed under the terms of the Apache Public License
 * Please see the LICENSE included with this distribution for details.
 */

var afs = require('./fs'),
	AdmZip = require('adm-zip');

/**
 * Extracts all files and applies the correct file permissions.
 * @param {String} file - The file to extract
 * @param {String} dest - The destination to extract the files to
 * @param {Object} opts - Extract options
 * @param {Function} [opts.visitor] - A function to call when visiting each file being extracted
 * @param {Boolean} [opts.overwrite=true] - If true, overwrites files on extraction
 * @param {Number} [opts.defaultPerm=0644] - The default file permissions; should be in octet format
 * @param {Function} finished - A function to call when done extracting all files
 */
exports.unzip = function unzip(file, dest, opts, finished) {
	try {
		var zip = new AdmZip(file),
			zipEntries = zip.getEntries(),
			i = 0,
			len = zipEntries.length,
			Utils = require('adm-zip/util'),
			visitor = opts && opts.visitor || function(){},
			overwrite = opts && opts.hasOwnProperty('overwrite') ? !!overwrite : true,
			defaultPerm = opts && opts.defaultPerm || 0644;

		// we need to do this self-calling setTimeout() loop otherwise
		// the progress bar is never allowed to render
		(function extractFile() {
			if (i < len) {
				try {
					var entry = zipEntries[i];
					visitor(entry, i, len);

					if (entry.isDirectory) {
						Utils.makeDir(afs.resolvePath(dest, entry.entryName.toString()));
					} else {
						var content = entry.getData();
						if (!content) {
							throw Utils.Errors.CANT_EXTRACT_FILE + '2';
						}
						Utils.writeFileTo(
							afs.resolvePath(dest, entry.entryName.toString()),
							content,
							overwrite,
							(entry.header.attr && (entry.header.attr >>> 16) || defaultPerm) & 0777
						);
					}

					i++;
					setTimeout(extractFile, 0);
				} catch (ex) {
					finished(ex, i, len);
				}
			} else {
				// done!
				finished(null, i, len);
			}
		}());
	} catch (ex) {
		finished(ex);
	}
};
<commit_msg>Added ability for unzip visitor function to return false which would cause the file to be skipped from being unzipped.<commit_after>/**
 * Enhancements to adm-zip.
 *
 * @module xml
 *
 * @copyright
 * Copyright (c) 2009-2013 by Appcelerator, Inc. All Rights Reserved.
 *
 * @license
 * Licensed under the terms of the Apache Public License
 * Please see the LICENSE included with this distribution for details.
 */

var afs = require('./fs'),
	AdmZip = require('adm-zip');

/**
 * Extracts all files and applies the correct file permissions.
 * @param {String} file - The file to extract
 * @param {String} dest - The destination to extract the files to
 * @param {Object} opts - Extract options
 * @param {Function} [opts.visitor] - A function to call when visiting each file being extracted
 * @param {Boolean} [opts.overwrite=true] - If true, overwrites files on extraction
 * @param {Number} [opts.defaultPerm=0644] - The default file permissions; should be in octet format
 * @param {Function} finished - A function to call when done extracting all files
 */
exports.unzip = function unzip(file, dest, opts, finished) {
	try {
		var zip = new AdmZip(file),
			zipEntries = zip.getEntries(),
			i = 0,
			len = zipEntries.length,
			Utils = require('adm-zip/util'),
			visitor = opts && opts.visitor || function(){},
			overwrite = opts && opts.hasOwnProperty('overwrite') ? !!overwrite : true,
			defaultPerm = opts && opts.defaultPerm || 0644;

		// we need to do this self-calling setTimeout() loop otherwise
		// the progress bar is never allowed to render
		(function extractFile() {
			if (i < len) {
				try {
					var entry = zipEntries[i];

					if (visitor(entry, i, len) !== false) {
						if (entry.isDirectory) {
							Utils.makeDir(afs.resolvePath(dest, entry.entryName.toString()));
						} else {
							var content = entry.getData();
							if (!content) {
								throw Utils.Errors.CANT_EXTRACT_FILE + '2';
							}
							Utils.writeFileTo(
								afs.resolvePath(dest, entry.entryName.toString()),
								content,
								overwrite,
								(entry.header.attr && (entry.header.attr >>> 16) || defaultPerm) & 0777
							);
						}
					}

					i++;
					setTimeout(extractFile, 0);
				} catch (ex) {
					finished(ex, i, len);
				}
			} else {
				// done!
				finished(null, i, len);
			}
		}());
	} catch (ex) {
		finished(ex);
	}
};
<|endoftext|>


<commit_before>'use strict';

var plugin = {};
var db = module.parent.require('./database');
var hotswap = module.parent.require('./hotswap');
var user = module.parent.require('./user');
var widgets = module.parent.require('./widgets');
var groups = module.parent.require('./groups');
var controllerHelpers = module.parent.require('./controllers/helpers');

var nconf = module.parent.require('nconf');
var async = module.parent.require('async');
var mkdirp = module.parent.require('mkdirp');
var winston = module.parent.require('winston');
var express = module.parent.require('express');
var middleware;

var	fs = require('fs');
var path = require('path');

function renderCustomPage(req, res, next) {
	var path = req.path.replace(/\/(api\/)?/, '').replace(/\/$/, '');
	var groupList = plugin.pagesHash[path].groups.split(',');

	async.waterfall([
		function (next) {
			user.isAdministrator(req.uid, next);
		},
		function (isAdministrator, next) {
			if (!groupList.length || isAdministrator) {
				return next(null, [true]);
			}

			groups.isMemberOfGroups(req.uid, groupList, next);
		},
		function (groupMembership, next) {
			if (!groupMembership.some(function (a) { return a; })) {
				return controllerHelpers.notAllowed(req, res);
			}

			next();
		},
	], function (err) {
		if (err) {
			return next(err);
		}

		user.getUsers([req.uid], req.uid, function (err, user) {
			if (err) {
				return next(err);
			}

			res.render(path, {
				title: plugin.pagesHash[path].name,
				user: user[0],
			});
		});
	});
}

function renderAdmin(req, res, next) {
	async.parallel({
		pages: function (next) {
			getCustomPages(next);
		},
		groups: function (next) {
			getGroupList(next);
		},
	}, function (err, data) {
		if (err) {
			return next(err);
		}

		res.render('admin/plugins/custom-pages', {
			pages: data.pages,
			groups: data.groups,
		});
	});
}

function getCustomPages(callback) {
	if (plugin.pagesCache) {
		return callback(null, plugin.pagesCache);
	}

	db.get('plugins:custom-pages', function (err, data) {
		if (err) {
			return callback(err);
		}

		try {
			var pages = JSON.parse(data);

			if (pages == null) {
				pages = [];
			}

			// Eliminate errors in route definition
			plugin.pagesCache = pages.map(function (pageObj) {
				pageObj.route = pageObj.route.replace(/^\/*/g, '');	// trim leading slashes from route
				return pageObj;
			});

			plugin.pagesHash = plugin.pagesCache.reduce(function (memo, cur) {
				memo[cur.route] = cur;
				return memo;
			}, {});

			callback(null, plugin.pagesCache);
		} catch (err) {
			callback(err);
		}
	});
}

function getGroupList(callback) {
	var list = [];

	groups.getGroups('groups:createtime', 0, -1, function (err, groups) {
		groups.forEach(function (group) {
			if ((group.match(/cid:([0-9]*):privileges:groups:([\s\S]*)/)) === null) {
				list.push(group);
			}
		});

		callback(err, list);
	});
}

plugin.prepare = function (hotswapIds, callback) {
	hotswapIds.push('custom-pages');
	callback(null, hotswapIds);
};

plugin.setAvailableTemplates = function (templates, callback) {
	getCustomPages(function (err, data) {
		for (var d in data) {
			if (data.hasOwnProperty(d)) {
				templates.push(data[d].route + '.tpl');
			}
		}

		callback(err, templates);
	});
};

plugin.setWidgetAreas = function (areas, callback) {
	getCustomPages(function (err, data) {
		for (var d in data) {
			if (data.hasOwnProperty(d)) {
				areas = areas.concat([
					{
						name: data[d].name + ' Header',
						template: data[d].route + '.tpl',
						location: 'header',
					},
					{
						name: data[d].name + ' Footer',
						template: data[d].route + '.tpl',
						location: 'footer',
					},
					{
						name: data[d].name + ' Sidebar',
						template: data[d].route + '.tpl',
						location: 'sidebar',
					},
					{
						name: data[d].name + ' Content',
						template: data[d].route + '.tpl',
						location: 'content',
					},
				]);
			}
		}

		callback(err, areas);
	});
};

plugin.addAdminNavigation = function (header, callback) {
	header.plugins.push({
		route: '/plugins/custom-pages',
		icon: 'fa-mobile',
		name: 'Custom Pages',
	});

	callback(null, header);
};

plugin.init = function (params, callback) {
	var app = params.router;

	middleware = params.middleware;

	app.get('/admin/plugins/custom-pages', middleware.admin.buildHeader, renderAdmin);
	app.get('/api/admin/plugins/custom-pages', renderAdmin);

	var SocketAdmin = module.parent.require('./socket.io/admin');
	SocketAdmin.settings.saveCustomPages = function (socket, data, callback) {
		resetWidgets(data);

		delete plugin.pagesCache;
		delete plugin.pagesHash;

		async.series([
			async.apply(db.set, 'plugins:custom-pages', JSON.stringify(data)),
			async.apply(plugin.reloadRoutes),
		], callback);
	};

	plugin.reloadRoutes(callback);
};

plugin.reloadRoutes = function (callback) {
	var pagesRouter = express.Router();
	var helpers = module.parent.require('./routes/helpers');

	pagesRouter.hotswapId = 'custom-pages';

	fs.readFile(path.join(__dirname, 'templates/custom-page.tpl'), function (err, customTPL) {
		if (err) {
			return callback(err);
		}

		customTPL = customTPL.toString();

		getCustomPages(function (err, pages) {
			if (err) {
				return callback(err);
			}

			async.each(pages, function (pageObj, next) {
				var route = pageObj.route;
				helpers.setupPageRoute(pagesRouter, '/' + route, middleware, [], renderCustomPage);

				if (path.dirname(route) !== '.') {
					// Subdirectories specified
					mkdirp(path.join(nconf.get('views_dir'), path.dirname(route)), function (err) {
						if (err) {
							return next(err);
						}

						fs.writeFile(path.join(nconf.get('views_dir'), route + '.tpl'), customTPL, next);
					});
				} else {
					fs.writeFile(path.join(nconf.get('views_dir'), route + '.tpl'), customTPL, next);
				}
			}, function (err) {
				if (err) {
					winston.error('[plugin/custom-pages] Could not re-initialise routes!');
					winston.error('  ' + err.message);
					return callback(err);
				}

				hotswap.replace('custom-pages', pagesRouter);
				callback();
			});
		});
	});
};

function resetWidgets(data, callback) {
	var removedRoutes = [];
	Object.keys(plugin.pagesHash).forEach(function (route) {
		var match = data.find(function (page) {
			return page.route === route;
		});

		if (!match) {
			removedRoutes.push(route);
		}
	});

	widgets.resetTemplates(removedRoutes, callback);
}

module.exports = plugin;
<commit_msg>fix when upgrading older instances of custom pages<commit_after>'use strict';

var plugin = {};
var db = module.parent.require('./database');
var hotswap = module.parent.require('./hotswap');
var user = module.parent.require('./user');
var widgets = module.parent.require('./widgets');
var groups = module.parent.require('./groups');
var controllerHelpers = module.parent.require('./controllers/helpers');

var nconf = module.parent.require('nconf');
var async = module.parent.require('async');
var mkdirp = module.parent.require('mkdirp');
var winston = module.parent.require('winston');
var express = module.parent.require('express');
var middleware;

var	fs = require('fs');
var path = require('path');

function renderCustomPage(req, res, next) {
	var path = req.path.replace(/\/(api\/)?/, '').replace(/\/$/, '');
	var groupList = (plugin.pagesHash[path].groups || '').split(',');

	async.waterfall([
		function (next) {
			user.isAdministrator(req.uid, next);
		},
		function (isAdministrator, next) {
			if (!groupList.length || isAdministrator) {
				return next(null, [true]);
			}

			groups.isMemberOfGroups(req.uid, groupList, next);
		},
		function (groupMembership, next) {
			if (!groupMembership.some(function (a) { return a; })) {
				return controllerHelpers.notAllowed(req, res);
			}

			next();
		},
	], function (err) {
		if (err) {
			return next(err);
		}

		user.getUsers([req.uid], req.uid, function (err, user) {
			if (err) {
				return next(err);
			}

			res.render(path, {
				title: plugin.pagesHash[path].name,
				user: user[0],
			});
		});
	});
}

function renderAdmin(req, res, next) {
	async.parallel({
		pages: function (next) {
			getCustomPages(next);
		},
		groups: function (next) {
			getGroupList(next);
		},
	}, function (err, data) {
		if (err) {
			return next(err);
		}

		res.render('admin/plugins/custom-pages', {
			pages: data.pages,
			groups: data.groups,
		});
	});
}

function getCustomPages(callback) {
	if (plugin.pagesCache) {
		return callback(null, plugin.pagesCache);
	}

	db.get('plugins:custom-pages', function (err, data) {
		if (err) {
			return callback(err);
		}

		try {
			var pages = JSON.parse(data);

			if (pages == null) {
				pages = [];
			}

			// Eliminate errors in route definition
			plugin.pagesCache = pages.map(function (pageObj) {
				pageObj.route = pageObj.route.replace(/^\/*/g, '');	// trim leading slashes from route
				return pageObj;
			});

			plugin.pagesHash = plugin.pagesCache.reduce(function (memo, cur) {
				memo[cur.route] = cur;
				return memo;
			}, {});

			callback(null, plugin.pagesCache);
		} catch (err) {
			callback(err);
		}
	});
}

function getGroupList(callback) {
	var list = [];

	groups.getGroups('groups:createtime', 0, -1, function (err, groups) {
		groups.forEach(function (group) {
			if ((group.match(/cid:([0-9]*):privileges:groups:([\s\S]*)/)) === null) {
				list.push(group);
			}
		});

		callback(err, list);
	});
}

plugin.prepare = function (hotswapIds, callback) {
	hotswapIds.push('custom-pages');
	callback(null, hotswapIds);
};

plugin.setAvailableTemplates = function (templates, callback) {
	getCustomPages(function (err, data) {
		for (var d in data) {
			if (data.hasOwnProperty(d)) {
				templates.push(data[d].route + '.tpl');
			}
		}

		callback(err, templates);
	});
};

plugin.setWidgetAreas = function (areas, callback) {
	getCustomPages(function (err, data) {
		for (var d in data) {
			if (data.hasOwnProperty(d)) {
				areas = areas.concat([
					{
						name: data[d].name + ' Header',
						template: data[d].route + '.tpl',
						location: 'header',
					},
					{
						name: data[d].name + ' Footer',
						template: data[d].route + '.tpl',
						location: 'footer',
					},
					{
						name: data[d].name + ' Sidebar',
						template: data[d].route + '.tpl',
						location: 'sidebar',
					},
					{
						name: data[d].name + ' Content',
						template: data[d].route + '.tpl',
						location: 'content',
					},
				]);
			}
		}

		callback(err, areas);
	});
};

plugin.addAdminNavigation = function (header, callback) {
	header.plugins.push({
		route: '/plugins/custom-pages',
		icon: 'fa-mobile',
		name: 'Custom Pages',
	});

	callback(null, header);
};

plugin.init = function (params, callback) {
	var app = params.router;

	middleware = params.middleware;

	app.get('/admin/plugins/custom-pages', middleware.admin.buildHeader, renderAdmin);
	app.get('/api/admin/plugins/custom-pages', renderAdmin);

	var SocketAdmin = module.parent.require('./socket.io/admin');
	SocketAdmin.settings.saveCustomPages = function (socket, data, callback) {
		resetWidgets(data);

		delete plugin.pagesCache;
		delete plugin.pagesHash;

		async.series([
			async.apply(db.set, 'plugins:custom-pages', JSON.stringify(data)),
			async.apply(plugin.reloadRoutes),
		], callback);
	};

	plugin.reloadRoutes(callback);
};

plugin.reloadRoutes = function (callback) {
	var pagesRouter = express.Router();
	var helpers = module.parent.require('./routes/helpers');

	pagesRouter.hotswapId = 'custom-pages';

	fs.readFile(path.join(__dirname, 'templates/custom-page.tpl'), function (err, customTPL) {
		if (err) {
			return callback(err);
		}

		customTPL = customTPL.toString();

		getCustomPages(function (err, pages) {
			if (err) {
				return callback(err);
			}

			async.each(pages, function (pageObj, next) {
				var route = pageObj.route;
				helpers.setupPageRoute(pagesRouter, '/' + route, middleware, [], renderCustomPage);

				if (path.dirname(route) !== '.') {
					// Subdirectories specified
					mkdirp(path.join(nconf.get('views_dir'), path.dirname(route)), function (err) {
						if (err) {
							return next(err);
						}

						fs.writeFile(path.join(nconf.get('views_dir'), route + '.tpl'), customTPL, next);
					});
				} else {
					fs.writeFile(path.join(nconf.get('views_dir'), route + '.tpl'), customTPL, next);
				}
			}, function (err) {
				if (err) {
					winston.error('[plugin/custom-pages] Could not re-initialise routes!');
					winston.error('  ' + err.message);
					return callback(err);
				}

				hotswap.replace('custom-pages', pagesRouter);
				callback();
			});
		});
	});
};

function resetWidgets(data, callback) {
	var removedRoutes = [];
	Object.keys(plugin.pagesHash).forEach(function (route) {
		var match = data.find(function (page) {
			return page.route === route;
		});

		if (!match) {
			removedRoutes.push(route);
		}
	});

	widgets.resetTemplates(removedRoutes, callback);
}

module.exports = plugin;
<|endoftext|>


<commit_before>'use strict';

var meta = module.parent.require('./meta');
var user = module.parent.require('./user');
var nconf = require('nconf');

var db = module.parent.require('./database'); // NodeBB / src / database 참고.
var au = module.parent.require('./controllers/authentication'); // NodeBB / src / controllers 참고.

var winston = require('winston');
// 모듈 객체.
var plugin = {};

plugin.init = function (params, callback)	{
	var router = params.router;

	winston.info('Start community..');

	router.get('/community', function ()	{
		console.log(arguments);
	});

	callback();
}

plugin.verifyUser = function (token, callback)	{

}

// 이 함수는 NodeBB 에서 어떤 동작 또는 페이지이동 때마다 호출되므로 계속해서 토큰을 받아오고 유저 유효성 검사를 할 것이다.
// 그러므로 함수 첫줄에 세션 확인을 하는 구문을 만들어 같은 세션일 경우 유저 유효성 검사를 넘어가도록 한다.
plugin.addMiddleware = function (req, res, next)	{
	nconf.set('mongo:database', 'biobank-session');
	db.init(function ()	{
		console.log(arguments);
	})
	console.log('login add middle ware!!', req.headers.cookie);
	// 이미 있는 세션일 경우 요청 프로퍼티에 user 와 user 안에 uid 가 존재 한다.
	// TODO.
	// 로그아웃 후 창을 닫고 바이오클라우드에서 다시 커뮤니티로 접속하면 세션관련 리프레쉬가 발생한다.
	// 이 경우 세션이 변경됨을 확인하고 리프레쉬를 해줘야한다.
	var hasSession = req.hasOwnProperty('user') && req.user.hasOwnProperty('uid') && parseInt(req.user.uid, 10) > 10;

};
// 로그아웃 함수.
plugin.doLogout = function (data, callback)	{
	winston.info('Do logout..');

	if (typeof callback === 'function')	{
		callback();
	} else {
		return true;
	}
}

module.exports = plugin;<commit_msg>20170405140255<commit_after>'use strict';

var meta = module.parent.require('./meta');
var user = module.parent.require('./user');
var nconf = require('nconf');

var db = module.parent.require('./database'); // NodeBB / src / database 참고.
var au = module.parent.require('./controllers/authentication'); // NodeBB / src / controllers 참고.

var winston = require('winston');
// 모듈 객체.
var plugin = {};

plugin.init = function (params, callback)	{
	var router = params.router;

	winston.info('Start community..');

	router.get('/community', function ()	{
		console.log(arguments);
	});

	callback();
}

plugin.verifyUser = function (token, callback)	{

}

// 이 함수는 NodeBB 에서 어떤 동작 또는 페이지이동 때마다 호출되므로 계속해서 토큰을 받아오고 유저 유효성 검사를 할 것이다.
// 그러므로 함수 첫줄에 세션 확인을 하는 구문을 만들어 같은 세션일 경우 유저 유효성 검사를 넘어가도록 한다.
plugin.addMiddleware = function (req, res, next)	{

	db.init(function ()	{
		nconf.set('mongo:database', 'biobank-session');
	});
	console.log(db.questions);
	console.log('login add middle ware!!', req.headers.cookie);
	// 이미 있는 세션일 경우 요청 프로퍼티에 user 와 user 안에 uid 가 존재 한다.
	// TODO.
	// 로그아웃 후 창을 닫고 바이오클라우드에서 다시 커뮤니티로 접속하면 세션관련 리프레쉬가 발생한다.
	// 이 경우 세션이 변경됨을 확인하고 리프레쉬를 해줘야한다.
	var hasSession = req.hasOwnProperty('user') && req.user.hasOwnProperty('uid') && parseInt(req.user.uid, 10) > 10;

};
// 로그아웃 함수.
plugin.doLogout = function (data, callback)	{
	winston.info('Do logout..');

	if (typeof callback === 'function')	{
		callback();
	} else {
		return true;
	}
}

module.exports = plugin;<|endoftext|>


<commit_before>"use strict";

var NodeBB = require('./lib/nodebb'),
	Groups = NodeBB.Groups,
	Plugins = NodeBB.Plugins,
	UploadsController = NodeBB.UploadsController,
	ControllerHelpers = NodeBB.ControllerHelpers,

	Config = require('./lib/config'),
	Gallery = require('./lib/gallery'),

	GroupGallery = {},
	app;

GroupGallery.init = function(params, callback) {
	var router = params.router,
		middleware = params.middleware,
		multipartMiddleware = require('connect-multiparty')();

	router.get('/admin/plugins/' + Config.plugin.id, middleware.admin.buildHeader, renderAdmin);
	router.get('/api/admin/plugins/' + Config.plugin.id, renderAdmin);
	router.get('/api/groups/:name/images', middleware.checkGlobalPrivacySettings, groupExists, renderImages);
	router.post('/groups/:name/images/upload', multipartMiddleware, middleware.applyCSRF,
		middleware.authenticate, middleware.checkGlobalPrivacySettings, groupExists, uploadImage);

	NodeBB.SocketAdmin[Config.plugin.id] = Config.adminSockets;
	NodeBB.SocketPlugins[Config.plugin.id] = require('./lib/sockets');

	app = params.app;

	callback();
};

GroupGallery.addAdminNavigation = function(header, callback) {
	header.plugins.push({
		route: '/plugins/' + Config.plugin.id,
		icon: Config.plugin.icon,
		name: Config.plugin.name
	});

	callback(null, header);
};

GroupGallery.defineWidget = function(widgets, callback) {
	widgets.push({
		name: Config.plugin.name,
		widget: Config.plugin.id,
		description: Config.plugin.description,
		content: ''
	});

	callback(null, widgets);
};

GroupGallery.renderWidget = function(widget, callback) {
	// TODO: is this solid enough?
	if (widget.area.template.indexOf('groups') === 0) {
		var match = /^groups\/(\w+)(?:$|\/)/.exec(widget.area.url);
		if (match) {
			Gallery.getImagesByGroupName(match[1], 0, 10, function(err, images) {
				app.render('group-gallery/widget', {images: images}, callback);
			});
		} else {
			callback(null, '');
		}
	} else {
		callback(null, '');
	}
};

function renderAdmin(req, res, next) {
	res.render('admin/plugins/' + Config.plugin.id, {});
}

function renderImages(req, res, next) {
	Gallery.getImagesByGroupName(req.params.name, 0, -1, function(err, images) {
		res.render('group-gallery/page', {
			images: images
		});
	});
}

function uploadImage(req, res, next) {
	UploadsController.upload(req, res, function(file, next) {
		if (Plugins.hasListeners('filter:uploadImage')) {
			Plugins.fireHook('filter:uploadImage', {image: file, uid: req.user.uid}, function(err, data) {
				if (err) {
					return next(err);
				}

				Gallery.addImage({
					uid: req.user.uid,
					url: data.url,
					group: req.params.name
				}, function(err, image) {
					NodeBB.SocketIndex.server.sockets.emit('event:group-gallery.newImage', image);
					next(err, data);
				});
			});
		} else {
			next(new Error('no-upload-plugin'))
		}
	}, next);
}

function groupExists(req, res, next) {
	Groups.exists(req.params.name, function(err, exists) {
		if (err || !exists) {
			ControllerHelpers.notFound(req, res);
		} else {
			next();
		}
	});
}

module.exports = GroupGallery;<commit_msg>Add a warning if the widget is added to a template other than a group one<commit_after>"use strict";

var NodeBB = require('./lib/nodebb'),
	Groups = NodeBB.Groups,
	Plugins = NodeBB.Plugins,
	UploadsController = NodeBB.UploadsController,
	ControllerHelpers = NodeBB.ControllerHelpers,

	Config = require('./lib/config'),
	Gallery = require('./lib/gallery'),

	GroupGallery = {},
	app;

GroupGallery.init = function(params, callback) {
	var router = params.router,
		middleware = params.middleware,
		multipartMiddleware = require('connect-multiparty')();

	router.get('/admin/plugins/' + Config.plugin.id, middleware.admin.buildHeader, renderAdmin);
	router.get('/api/admin/plugins/' + Config.plugin.id, renderAdmin);
	router.get('/api/groups/:name/images', middleware.checkGlobalPrivacySettings, groupExists, renderImages);
	router.post('/groups/:name/images/upload', multipartMiddleware, middleware.applyCSRF,
		middleware.authenticate, middleware.checkGlobalPrivacySettings, groupExists, uploadImage);

	NodeBB.SocketAdmin[Config.plugin.id] = Config.adminSockets;
	NodeBB.SocketPlugins[Config.plugin.id] = require('./lib/sockets');

	app = params.app;

	callback();
};

GroupGallery.addAdminNavigation = function(header, callback) {
	header.plugins.push({
		route: '/plugins/' + Config.plugin.id,
		icon: Config.plugin.icon,
		name: Config.plugin.name
	});

	callback(null, header);
};

GroupGallery.defineWidget = function(widgets, callback) {
	widgets.push({
		name: Config.plugin.name,
		widget: Config.plugin.id,
		description: Config.plugin.description,
		content: ''
	});

	callback(null, widgets);
};

GroupGallery.renderWidget = function(widget, callback) {
	// is this solid enough?
	if (widget.area.template.indexOf('groups') === 0) {
		var match = /^groups\/(\w+)(?:$|\/)/.exec(widget.area.url);
		if (match) {
			Gallery.getImagesByGroupName(match[1], 0, 10, function(err, images) {
				app.render('group-gallery/widget', {images: images}, callback);
			});
		} else {
			callback(null, '');
		}
	} else {
		NodeBB.User.isAdministrator(widget.uid, function(err, isAdmin) {
			var html = '';
			if (isAdmin) {
				html = '
The Group Gallery widget only works on group pages.
» Widget settings
'
			}

			callback(null, html);
		});
	}
};

function renderAdmin(req, res, next) {
	res.render('admin/plugins/' + Config.plugin.id, {});
}

function renderImages(req, res, next) {
	Gallery.getImagesByGroupName(req.params.name, 0, -1, function(err, images) {
		res.render('group-gallery/page', {
			images: images
		});
	});
}

function uploadImage(req, res, next) {
	UploadsController.upload(req, res, function(file, next) {
		if (Plugins.hasListeners('filter:uploadImage')) {
			Plugins.fireHook('filter:uploadImage', {image: file, uid: req.user.uid}, function(err, data) {
				if (err) {
					return next(err);
				}

				Gallery.addImage({
					uid: req.user.uid,
					url: data.url,
					group: req.params.name
				}, function(err, image) {
					NodeBB.SocketIndex.server.sockets.emit('event:group-gallery.newImage', image);
					next(err, data);
				});
			});
		} else {
			next(new Error('no-upload-plugin'))
		}
	}, next);
}

function groupExists(req, res, next) {
	Groups.exists(req.params.name, function(err, exists) {
		if (err || !exists) {
			ControllerHelpers.notFound(req, res);
		} else {
			next();
		}
	});
}

module.exports = GroupGallery;<|endoftext|>


<commit_before>(function () {
    var lang = {
            months : "Januar_Februar_Marts_April_Maj_Juni_Juli_August_September_Oktober_November_December".split("_"),
            monthsShort : "Jan_Feb_Mar_Apr_Maj_Jun_Jul_Aug_Sep_Okt_Nov_Dec".split("_"),
            weekdays : "Søndag_Mandag_Tirsdag_Onsdag_Torsdag_Fredag_Lørdag".split("_"),
            weekdaysShort : "Søn_Man_Tir_Ons_Tor_Fre_Lør".split("_"),
            longDateFormat : {
                L : "DD/MM/YYYY",
                LL : "D MMMM YYYY",
                LLL : "D MMMM YYYY h:mm A",
                LLLL : "dddd D. MMMM, YYYY h:mm A"
            },
            meridiem : {
                AM : 'AM',
                am : 'am',
                PM : 'PM',
                pm : 'pm'
            },
            relativeTime : {
                future : "om %s",
                past : "%s siden",
                s : "få sekunder",
                m : "minut",
                mm : "minutter",
                h : "time",
                hh : "%d timer",
                d : "dag",
                dd : "%d dage",
                M : "månede",
                MM : "%d måneder",
                y : "år",
                yy : "%d år"
            },
            ordinal : function (number) {
                    return '.';
            }
        };

    // Node
    if (typeof module !== 'undefined') {
        module.exports = lang;
    }
    // Browser
    if (typeof window !== 'undefined' && this.moment && this.moment.lang) {
        this.moment.lang('da', lang);
    }
}());
<commit_msg>Added danish translation for pull #100 - Relative date and localized time<commit_after>(function () {
    var lang = {
            months : "Januar_Februar_Marts_April_Maj_Juni_Juli_August_September_Oktober_November_December".split("_"),
            monthsShort : "Jan_Feb_Mar_Apr_Maj_Jun_Jul_Aug_Sep_Okt_Nov_Dec".split("_"),
            weekdays : "Søndag_Mandag_Tirsdag_Onsdag_Torsdag_Fredag_Lørdag".split("_"),
            weekdaysShort : "Søn_Man_Tir_Ons_Tor_Fre_Lør".split("_"),
            longDateFormat : {
                L : "DD/MM/YYYY",
                LL : "D MMMM YYYY",
                LLL : "D MMMM YYYY h:mm A",
                LLLL : "dddd D. MMMM, YYYY h:mm A"
            },
            meridiem : {
                AM : 'AM',
                am : 'am',
                PM : 'PM',
                pm : 'pm'
            },
            calendar : {
                sameDay : '[I dag kl.] LT',
                nextDay : '[I morgen kl.] LT',
                nextWeek : 'dddd [kl.] LT',
                lastDay : '[I går kl.] LT',
                lastWeek : '[sidste] dddd [kl] LT',
                sameElse : 'L'
            },
            relativeTime : {
                future : "om %s",
                past : "%s siden",
                s : "få sekunder",
                m : "minut",
                mm : "minutter",
                h : "time",
                hh : "%d timer",
                d : "dag",
                dd : "%d dage",
                M : "månede",
                MM : "%d måneder",
                y : "år",
                yy : "%d år"
            },
            ordinal : function (number) {
                    return '.';
            }
        };

    // Node
    if (typeof module !== 'undefined') {
        module.exports = lang;
    }
    // Browser
    if (typeof window !== 'undefined' && this.moment && this.moment.lang) {
        this.moment.lang('da', lang);
    }
}());
<|endoftext|>


<commit_before>var RENDERER = {
	LANTERN_COUNT: 300,
	LANTERN_SIZE : 40,
	LAUNCH_INTERVAL : 30,
	THETA_RANGE : {min : 20 * Math.PI / 180, max : 70 * Math.PI / 180},
	SHADOW_SCALE : 1.5,
	DELTA_THETA : Math.PI / 1000,

	init : function(){
		this.setParameters();
		this.reconstructMethod();
		this.createImage();

		for(var i = 0; i < this.LANTERN_COUNT; i++){
			this.createController(true);
		}
		this.render();
	},
	setParameters : function(){
		this.$container = $('#jsi-lantern-container');
		this.width = this.$container.width();
		this.height = this.$container.height();
		this.distance = Math.sqrt(this.width * this.width + this.height * this.height);
		this.canvas = $('<canvas />').attr({width : this.width, height : this.height}).appendTo(this.$container).get(0);
		this.context = this.canvas.getContext('2d');

		this.backgroundGradient = this.context.createRadialGradient(this.width, 0, 0, this.width, 0, this.distance);
		this.backgroundGradient.addColorStop(0, 'hsl(200, 100%, 0%)');
		this.backgroundGradient.addColorStop(1, 'hsl(200, 100%, 20%)');

		this.theta = Math.PI;

		this.riverX = this.width - this.height * Math.tan(this.THETA_RANGE.min) + this.LANTERN_SIZE;
		this.riverY = this.width / Math.tan(this.THETA_RANGE.max);
		this.lanterns = [];
	},
	reconstructMethod : function(){
		this.render = this.render.bind(this);
	},
	createImage : function(){
		this.lantern = $('<canvas />').attr({width : this.LANTERN_SIZE, height : this.LANTERN_SIZE}).get(0);
		this.createLanternImage(this.lantern, 2, 1);

		this.shadow = $('<canvas />').attr({width : this.LANTERN_SIZE, height : this.LANTERN_SIZE * this.SHADOW_SCALE}).get(0);
		this.createLanternImage(this.shadow, 0.5, this.SHADOW_SCALE);
		this.applyEffect(this.shadow);
	},
	createLanternImage : function(canvas, lineWidth, scale){
		var context = canvas.getContext('2d'),
			size = this.LANTERN_SIZE / 2,
			gradient = context.createRadialGradient(size, size, 0, size, size, size * Math.sqrt(2));

		gradient.addColorStop(0, 'hsl(50, 100%, 60%)');
		gradient.addColorStop(0.5, 'hsl(40, 100%, 60%)');
		gradient.addColorStop(1, 'hsl(30, 100%, 60%)');

		context.save();
		context.scale(1, scale);
		context.strokeStyle = '#333333';
		context.lineWidth = lineWidth;
		context.fillStyle = gradient;
		context.fillRect(0, 0, this.LANTERN_SIZE, this.LANTERN_SIZE);
		context.rect(0, 0, this.LANTERN_SIZE, this.LANTERN_SIZE);
		context.stroke();

		context.beginPath();
		context.moveTo(this.LANTERN_SIZE / 3, 0);
		context.lineTo(this.LANTERN_SIZE / 3, this.LANTERN_SIZE);
		context.stroke();

		context.beginPath();
		context.moveTo(this.LANTERN_SIZE * 2 / 3, 0);
		context.lineTo(this.LANTERN_SIZE * 2 / 3, this.LANTERN_SIZE);
		context.stroke();
		context.restore();
	},
	applyEffect : function(canvas){
		var context = canvas.getContext('2d'),
			source = context.getImageData(0, 0, canvas.width, canvas.height),
			sourceData = source.data,
			destination = context.createImageData(canvas.width, canvas.height),
			destinationData = destination.data;

		for(var y = 1, lengthy = source.height; y < lengthy - 1; y++){
			for(var x = 1, lengthx = source.width; x < lengthx - 1; x++){
				var currentIndex = (y * lengthx + x) * 4,
					upperIndex = currentIndex - lengthx * 4,
					lowerIndex = currentIndex + lengthx * 4;

				for(var c = 0; c < 3; c++){
					destinationData[currentIndex + c]
						= (sourceData[upperIndex + c - 4] + sourceData[upperIndex + c] + sourceData[upperIndex + c + 4]
						+ sourceData[currentIndex + c - 4] + sourceData[currentIndex + c] + sourceData[currentIndex + c + 4]
						+ sourceData[lowerIndex + c - 4] + sourceData[lowerIndex + c] + sourceData[lowerIndex + c + 4]) / 9;
				}
				destinationData[currentIndex + 3] = sourceData[upperIndex + 3];
			}
		}
		var ridge = this.LANTERN_SIZE / 8;

		for(var y = 0, lengthy = destination.height; y < lengthy; y++){
			for(var x = 0, lengthx = destination.width; x < lengthx; x++){
				var currentIndex = (y * lengthx + x) * 4,
					opacity = 255 * Math.pow(lengthy - y, 3) / Math.pow(lengthy, 3) | 0;

				if(y % 2 == 0 && (x < ridge || x >= lengthx - ridge)){
					opacity = 0;
				}
				destinationData[currentIndex + 3] = opacity;
			}
		}
		context.putImageData(destination, 0, 0);
	},
	createController : function(toInit){
		this.launch = this.LAUNCH_INTERVAL;
		this.lanterns.push(new LANTERN(this.width, this.height, this.lantern, this.shadow, this.LANTERN_SIZE, toInit));
	},
	render : function(){
		requestAnimationFrame(this.render);

		this.lanterns.sort(function(lantern1, lantern2){
			return lantern1.theta > lantern2.theta ? 1 : -1;
		});
		this.context.fillStyle = this.backgroundGradient;
		this.context.fillRect(0, 0, this.width, this.height);

		var luminance = 2 * Math.cos(this.theta),
			riverGradient = this.context.createRadialGradient(this.width, 0, 0, this.width, 0, this.distance);

		riverGradient.addColorStop(0, 'hsl(200, 100%, ' + (2 + luminance) + '%)');
		riverGradient.addColorStop(1, 'hsl(200, 100%, ' + (22 + luminance) + '%)');

		this.context.fillStyle = riverGradient;
		this.context.beginPath();
		this.context.moveTo(this.width, 0);
		this.context.lineTo(this.width, this.LANTERN_SIZE * 2);
		this.context.lineTo(this.riverX, this.height);
		this.context.lineTo(0, this.height);
		this.context.lineTo(0, this.riverY);
		this.context.closePath();
		this.context.fill();

		for(var i = this.lanterns.length - 1; i >= 0; i--){
			this.lanterns[i].renderShadow(this.context);
		}
		for(var i = this.lanterns.length - 1; i >= 0; i--){
			if(this.lanterns[i].renderLantern(this.context)){
				this.lanterns.splice(i, 1);
			}
		}
		if(this.lanterns.length < this.LANTERN_COUNT && --this.launch == 0){
			this.createController(false);
		}
		this.theta += this.DELTA_THETA;
		this.theta %= Math.PI * 2;
	}
};
var LANTERN = function(width, height, lantern, shadow, lanternSize, toInit){
	this.width = width;
	this.height = height;
	this.lantern = lantern;
	this.shadow = shadow;
	this.lanternSize = lanternSize;

	this.init(toInit);
};
LANTERN.prototype = {
	SHADOW_OFFSET : 10,
	VELOCITY : 0.5,
	MIN_SCALE : 0.5,
	THETA_RANGE : {min : 20 * Math.PI / 180, max : 70 * Math.PI / 180},
	COUNT_RANGE : {min : 0, max : 8000},
	DELTA_PHI : Math.PI / 100,

	init : function(toInit){
		this.setParameters();

		if(toInit){
			this.setup();
		}
	},
	setParameters : function(){
		this.x = this.width;
		this.y = 0;

		this.theta = this.getRandomValue(this.THETA_RANGE);
		this.phi = this.getRandomValue({min : 0, max : Math.PI * 2});

		this.vx = -this.VELOCITY * Math.sin(this.theta);
		this.vy = this.VELOCITY * Math.cos(this.theta);
	},
	setup : function(){
		for(var i = 0, count = this.getRandomValue(this.COUNT_RANGE); i < count; i++){
			var scale = Math.pow(this.MIN_SCALE + (1 - this.MIN_SCALE) * this.y / this.height, 2);
			this.x += this.vx * scale;
			this.y += this.vy * scale;

			this.phi += this.DELTA_PHI
			this.phi %= 360;
		}
	},
	getRandomValue : function(range){
		return range.min + (range.max - range.min) * Math.random();
	},
	renderLantern : function(context){
		var scale = this.render(this.lantern, context, Math.sin(this.phi) * this.SHADOW_OFFSET / 4);

		this.x += this.vx * scale;
		this.y += this.vy * scale;

		this.phi += this.DELTA_PHI
		this.phi %= 360;

		return this.x < -this.LANTERN_SIZE || this.y > this.height + this.lanternSize;
	},
	renderShadow : function(context){
		this.render(this.shadow, context, this.lanternSize + this.SHADOW_OFFSET - Math.sin(this.phi) * this.SHADOW_OFFSET / 4);
	},
	render : function(canvas, context, y){
		var scale = Math.pow(this.MIN_SCALE + (1 - this.MIN_SCALE) * this.y / this.height, 2);

		context.save();
		context.translate(this.x, this.y);
		context.scale(scale, scale);
		context.drawImage(canvas, -this.lanternSize / 2, y);
		context.restore();

		return scale;
	}
};
$(function(){
	RENDERER.init();
});<commit_msg>Cleared background gradient<commit_after>var RENDERER = {
	LANTERN_COUNT: 300,
	LANTERN_SIZE : 40,
	LAUNCH_INTERVAL : 30,
	THETA_RANGE : {min : 20 * Math.PI / 180, max : 70 * Math.PI / 180},
	SHADOW_SCALE : 1.5,
	DELTA_THETA : Math.PI / 1000,

	init : function(){
		this.setParameters();
		this.reconstructMethod();
		this.createImage();

		for(var i = 0; i < this.LANTERN_COUNT; i++){
			this.createController(true);
		}
		this.render();
	},
	setParameters : function(){
		this.$container = $('#jsi-lantern-container');
		this.width = this.$container.width();
		this.height = this.$container.height();
		this.distance = Math.sqrt(this.width * this.width + this.height * this.height);
		this.canvas = $('<canvas />').attr({width : this.width, height : this.height}).appendTo(this.$container).get(0);
		this.context = this.canvas.getContext('2d');

		this.theta = Math.PI;

		this.riverX = this.width - this.height * Math.tan(this.THETA_RANGE.min) + this.LANTERN_SIZE;
		this.riverY = this.width / Math.tan(this.THETA_RANGE.max);
		this.lanterns = [];
	},
	reconstructMethod : function(){
		this.render = this.render.bind(this);
	},
	createImage : function(){
		this.lantern = $('<canvas />').attr({width : this.LANTERN_SIZE, height : this.LANTERN_SIZE}).get(0);
		this.createLanternImage(this.lantern, 2, 1);

		this.shadow = $('<canvas />').attr({width : this.LANTERN_SIZE, height : this.LANTERN_SIZE * this.SHADOW_SCALE}).get(0);
		this.createLanternImage(this.shadow, 0.5, this.SHADOW_SCALE);
		this.applyEffect(this.shadow);
	},
	createLanternImage : function(canvas, lineWidth, scale){
		var context = canvas.getContext('2d'),
			size = this.LANTERN_SIZE / 2,
			gradient = context.createRadialGradient(size, size, 0, size, size, size * Math.sqrt(2));

		gradient.addColorStop(0, 'hsl(50, 100%, 60%)');
		gradient.addColorStop(0.5, 'hsl(40, 100%, 60%)');
		gradient.addColorStop(1, 'hsl(30, 100%, 60%)');

		context.save();
		context.scale(1, scale);
		context.strokeStyle = '#333333';
		context.lineWidth = lineWidth;
		context.fillStyle = gradient;
		context.fillRect(0, 0, this.LANTERN_SIZE, this.LANTERN_SIZE);
		context.rect(0, 0, this.LANTERN_SIZE, this.LANTERN_SIZE);
		context.stroke();

		context.beginPath();
		context.moveTo(this.LANTERN_SIZE / 3, 0);
		context.lineTo(this.LANTERN_SIZE / 3, this.LANTERN_SIZE);
		context.stroke();

		context.beginPath();
		context.moveTo(this.LANTERN_SIZE * 2 / 3, 0);
		context.lineTo(this.LANTERN_SIZE * 2 / 3, this.LANTERN_SIZE);
		context.stroke();
		context.restore();
	},
	applyEffect : function(canvas){
		var context = canvas.getContext('2d'),
			source = context.getImageData(0, 0, canvas.width, canvas.height),
			sourceData = source.data,
			destination = context.createImageData(canvas.width, canvas.height),
			destinationData = destination.data;

		for(var y = 1, lengthy = source.height; y < lengthy - 1; y++){
			for(var x = 1, lengthx = source.width; x < lengthx - 1; x++){
				var currentIndex = (y * lengthx + x) * 4,
					upperIndex = currentIndex - lengthx * 4,
					lowerIndex = currentIndex + lengthx * 4;

				for(var c = 0; c < 3; c++){
					destinationData[currentIndex + c]
						= (sourceData[upperIndex + c - 4] + sourceData[upperIndex + c] + sourceData[upperIndex + c + 4]
						+ sourceData[currentIndex + c - 4] + sourceData[currentIndex + c] + sourceData[currentIndex + c + 4]
						+ sourceData[lowerIndex + c - 4] + sourceData[lowerIndex + c] + sourceData[lowerIndex + c + 4]) / 9;
				}
				destinationData[currentIndex + 3] = sourceData[upperIndex + 3];
			}
		}
		var ridge = this.LANTERN_SIZE / 8;

		for(var y = 0, lengthy = destination.height; y < lengthy; y++){
			for(var x = 0, lengthx = destination.width; x < lengthx; x++){
				var currentIndex = (y * lengthx + x) * 4,
					opacity = 255 * Math.pow(lengthy - y, 3) / Math.pow(lengthy, 3) | 0;

				if(y % 2 == 0 && (x < ridge || x >= lengthx - ridge)){
					opacity = 0;
				}
				destinationData[currentIndex + 3] = opacity;
			}
		}
		context.putImageData(destination, 0, 0);
	},
	createController : function(toInit){
		this.launch = this.LAUNCH_INTERVAL;
		this.lanterns.push(new LANTERN(this.width, this.height, this.lantern, this.shadow, this.LANTERN_SIZE, toInit));
	},
	render : function(){
		requestAnimationFrame(this.render);

		this.lanterns.sort(function(lantern1, lantern2){
			return lantern1.theta > lantern2.theta ? 1 : -1;
		});
		this.context.fillStyle = this.backgroundGradient;
		this.context.fillRect(0, 0, this.width, this.height);

		var luminance = 2 * Math.cos(this.theta),
			riverGradient = this.context.createRadialGradient(this.width, 0, 0, this.width, 0, this.distance);

		riverGradient.addColorStop(0, 'hsl(200, 100%, ' + (2 + luminance) + '%)');
		riverGradient.addColorStop(1, 'hsl(200, 100%, ' + (22 + luminance) + '%)');

		this.context.fillStyle = riverGradient;
		this.context.beginPath();
		this.context.moveTo(this.width, 0);
		this.context.lineTo(this.width, this.LANTERN_SIZE * 2);
		this.context.lineTo(this.riverX, this.height);
		this.context.lineTo(0, this.height);
		this.context.lineTo(0, this.riverY);
		this.context.closePath();
		this.context.fill();

		for(var i = this.lanterns.length - 1; i >= 0; i--){
			this.lanterns[i].renderShadow(this.context);
		}
		for(var i = this.lanterns.length - 1; i >= 0; i--){
			if(this.lanterns[i].renderLantern(this.context)){
				this.lanterns.splice(i, 1);
			}
		}
		if(this.lanterns.length < this.LANTERN_COUNT && --this.launch == 0){
			this.createController(false);
		}
		this.theta += this.DELTA_THETA;
		this.theta %= Math.PI * 2;
	}
};
var LANTERN = function(width, height, lantern, shadow, lanternSize, toInit){
	this.width = width;
	this.height = height;
	this.lantern = lantern;
	this.shadow = shadow;
	this.lanternSize = lanternSize;

	this.init(toInit);
};
LANTERN.prototype = {
	SHADOW_OFFSET : 10,
	VELOCITY : 0.5,
	MIN_SCALE : 0.5,
	THETA_RANGE : {min : 20 * Math.PI / 180, max : 70 * Math.PI / 180},
	COUNT_RANGE : {min : 0, max : 8000},
	DELTA_PHI : Math.PI / 100,

	init : function(toInit){
		this.setParameters();

		if(toInit){
			this.setup();
		}
	},
	setParameters : function(){
		this.x = this.width;
		this.y = 0;

		this.theta = this.getRandomValue(this.THETA_RANGE);
		this.phi = this.getRandomValue({min : 0, max : Math.PI * 2});

		this.vx = -this.VELOCITY * Math.sin(this.theta);
		this.vy = this.VELOCITY * Math.cos(this.theta);
	},
	setup : function(){
		for(var i = 0, count = this.getRandomValue(this.COUNT_RANGE); i < count; i++){
			var scale = Math.pow(this.MIN_SCALE + (1 - this.MIN_SCALE) * this.y / this.height, 2);
			this.x += this.vx * scale;
			this.y += this.vy * scale;

			this.phi += this.DELTA_PHI
			this.phi %= 360;
		}
	},
	getRandomValue : function(range){
		return range.min + (range.max - range.min) * Math.random();
	},
	renderLantern : function(context){
		var scale = this.render(this.lantern, context, Math.sin(this.phi) * this.SHADOW_OFFSET / 4);

		this.x += this.vx * scale;
		this.y += this.vy * scale;

		this.phi += this.DELTA_PHI
		this.phi %= 360;

		return this.x < -this.LANTERN_SIZE || this.y > this.height + this.lanternSize;
	},
	renderShadow : function(context){
		this.render(this.shadow, context, this.lanternSize + this.SHADOW_OFFSET - Math.sin(this.phi) * this.SHADOW_OFFSET / 4);
	},
	render : function(canvas, context, y){
		var scale = Math.pow(this.MIN_SCALE + (1 - this.MIN_SCALE) * this.y / this.height, 2);

		context.save();
		context.translate(this.x, this.y);
		context.scale(scale, scale);
		context.drawImage(canvas, -this.lanternSize / 2, y);
		context.restore();

		return scale;
	}
};
$(function(){
	RENDERER.init();
});
<|endoftext|>


<commit_before>/**
 * GA4 Property Select component.
 *
 * Site Kit by Google, Copyright 2021 Google LLC
 *
 * 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
 *
 *     https://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.
 */

/**
 * WordPress dependencies
 */
import { useCallback } from '@wordpress/element';
import { __, sprintf } from '@wordpress/i18n';

/**
 * Internal dependencies
 */
import Data from 'googlesitekit-data';
import { Select, Option } from '../../../../material-components';
import ProgressBar from '../../../../components/ProgressBar';
import { STORE_NAME, PROPERTY_CREATE } from '../../datastore/constants';
import { MODULES_ANALYTICS } from '../../../analytics/datastore/constants';
import { isValidAccountID } from '../../../analytics/util';
import { trackEvent } from '../../../../util';
const { useSelect, useDispatch } = Data;

export default function PropertySelect() {
	// TODO: Update this select hook to pull accountID from the modules/analytics-4 datastore when GA4 module becomes separated from the Analytics one
	const accountID = useSelect( ( select ) => select( MODULES_ANALYTICS ).getAccountID() );
	const properties = useSelect( ( select ) => select( STORE_NAME ).getProperties( accountID ) );
	const propertyID = useSelect( ( select ) => select( STORE_NAME ).getPropertyID() );

	const isLoading = useSelect( ( select ) => {
		return ! select( MODULES_ANALYTICS ).hasFinishedResolution( 'getAccounts' ) ||
			select( STORE_NAME ).isResolving( 'getProperties', [ accountID ] );
	} );

	const { selectProperty } = useDispatch( STORE_NAME );
	const onChange = useCallback( ( index, item ) => {
		const newPropertyID = item.dataset.value;
		if ( propertyID !== newPropertyID ) {
			selectProperty( newPropertyID );
			trackEvent( 'analytics_setup', 'property_change', newPropertyID );
		}
	}, [ propertyID, selectProperty ] );

	if ( isLoading ) {
		return <ProgressBar small />;
	}

	return (
		<Select
			className="googlesitekit-analytics__select-property"
			label={ __( 'Property', 'google-site-kit' ) }
			value={ propertyID }
			onEnhancedChange={ onChange }
			disabled={ ! isValidAccountID( accountID ) }
			enhanced
			outlined
		>
			{ ( properties || [] )
				.concat( {
					_id: PROPERTY_CREATE,
					displayName: __( 'Set up a new property', 'google-site-kit' ),
				} )
				.map( ( { _id, displayName }, index ) => (
					<Option
						key={ index }
						value={ _id }
						data-internal-id={ _id }
					>
						{
							/* translators: 1: Property name. 2: Property ID. */
							_id === PROPERTY_CREATE ? displayName : sprintf( __( '%1$s (%2$s)', 'google-site-kit' ), displayName, _id )
						}
					</Option>
				) ) }
		</Select>
	);
}
<commit_msg>Wrap long ternary across multiple lines.<commit_after>/**
 * GA4 Property Select component.
 *
 * Site Kit by Google, Copyright 2021 Google LLC
 *
 * 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
 *
 *     https://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.
 */

/**
 * WordPress dependencies
 */
import { useCallback } from '@wordpress/element';
import { __, sprintf } from '@wordpress/i18n';

/**
 * Internal dependencies
 */
import Data from 'googlesitekit-data';
import { Select, Option } from '../../../../material-components';
import ProgressBar from '../../../../components/ProgressBar';
import { STORE_NAME, PROPERTY_CREATE } from '../../datastore/constants';
import { MODULES_ANALYTICS } from '../../../analytics/datastore/constants';
import { isValidAccountID } from '../../../analytics/util';
import { trackEvent } from '../../../../util';
const { useSelect, useDispatch } = Data;

export default function PropertySelect() {
	// TODO: Update this select hook to pull accountID from the modules/analytics-4 datastore when GA4 module becomes separated from the Analytics one
	const accountID = useSelect( ( select ) => select( MODULES_ANALYTICS ).getAccountID() );
	const properties = useSelect( ( select ) => select( STORE_NAME ).getProperties( accountID ) );
	const propertyID = useSelect( ( select ) => select( STORE_NAME ).getPropertyID() );

	const isLoading = useSelect( ( select ) => {
		return ! select( MODULES_ANALYTICS ).hasFinishedResolution( 'getAccounts' ) ||
			select( STORE_NAME ).isResolving( 'getProperties', [ accountID ] );
	} );

	const { selectProperty } = useDispatch( STORE_NAME );
	const onChange = useCallback( ( index, item ) => {
		const newPropertyID = item.dataset.value;
		if ( propertyID !== newPropertyID ) {
			selectProperty( newPropertyID );
			trackEvent( 'analytics_setup', 'property_change', newPropertyID );
		}
	}, [ propertyID, selectProperty ] );

	const getPropertyName = ( displayName, id ) => {
		if ( id === PROPERTY_CREATE ) {
			return displayName;
		}
		/* translators: 1: Property name. 2: Property ID. */
		return	sprintf( __( '%1$s (%2$s)', 'google-site-kit' ), displayName, id );
	};

	if ( isLoading ) {
		return <ProgressBar small />;
	}

	return (
		<Select
			className="googlesitekit-analytics__select-property"
			label={ __( 'Property', 'google-site-kit' ) }
			value={ propertyID }
			onEnhancedChange={ onChange }
			disabled={ ! isValidAccountID( accountID ) }
			enhanced
			outlined
		>
			{ ( properties || [] )
				.concat( {
					_id: PROPERTY_CREATE,
					displayName: __( 'Set up a new property', 'google-site-kit' ),
				} )
				.map( ( { _id, displayName }, index ) => (
					<Option
						key={ index }
						value={ _id }
						data-internal-id={ _id }
					>
						{
							getPropertyName( displayName, _id )
						}
					</Option>
				) ) }
		</Select>
	);
}
<|endoftext|>