<commit_before>/*jslint indent: 2, nomen: true, maxlen: 120, vars: true */
/*global module, require, exports */

////////////////////////////////////////////////////////////////////////////////
/// @brief Foxx BaseMiddleware
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2013 triagens GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
///     http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is triAGENS GmbH, Cologne, Germany
///
/// @author Lucas Dohmen
/// @author Copyright 2013, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////

var BaseMiddleware;

////////////////////////////////////////////////////////////////////////////////
/// @fn JSF_foxx_BaseMiddleware_initializer
/// @brief The Base Middleware
///
/// The `BaseMiddleware` manipulates the request and response
/// objects to give you a nicer API.
////////////////////////////////////////////////////////////////////////////////

BaseMiddleware = function () {
  'use strict';
  var middleware = function (request, response, options, next) {
    var responseFunctions,
      requestFunctions,
      trace,
      _ = require("underscore"),
      console = require("console"),
      actions = require("org/arangodb/actions");

    requestFunctions = {

////////////////////////////////////////////////////////////////////////////////
/// @fn JSF_foxx_BaseMiddleware_request_body
/// @brief Get the JSON parsed body of the request
///
/// @FUN{request.body()}
///
/// Get the JSON parsed body of the request. If you need the raw version, please
/// refer to the `rawBody` function.
////////////////////////////////////////////////////////////////////////////////

      body: function () {
        return JSON.parse(this.requestBody);
      },

////////////////////////////////////////////////////////////////////////////////
/// @fn JSF_foxx_BaseMiddleware_request_rawBody
/// @brief Get the raw body of the request
///
/// @FUN{request.rawBody()}
///
/// The raw request body, not parsed. Just a String.
////////////////////////////////////////////////////////////////////////////////

      rawBody: function () {
        return this.requestBody;
      },

////////////////////////////////////////////////////////////////////////////////
/// @fn JSF_foxx_BaseMiddleware_request_params
/// @brief The jinxed `params` function
///
/// @FUN{request.params(@FA{key})}
///
/// Get the parameters of the request. This process is two-fold:
///
/// - If you have defined an URL like `/test/:id` and the user requested
///   `/test/1`, the call `params("id")` will return `1`.
/// - If you have defined an URL like `/test` and the user gives a query
///   component, the query parameters will also be returned.  So for example if
///   the user requested `/test?a=2`, the call `params("a")` will return `2`.
////////////////////////////////////////////////////////////////////////////////

      params: function (key) {
        var ps = {};
        _.extend(ps, this.urlParameters);
        _.extend(ps, this.parameters);
        return ps[key];
      }
    };

    responseFunctions = {

////////////////////////////////////////////////////////////////////////////////
/// @fn JSF_foxx_BaseMiddleware_response_status
/// @brief The straightforward `status` function
///
/// @FUN{response.status(@FA{code})}
///
/// Set the status @FA{code} of your response, for example:
///
/// *Examples*
///
/// @code
///    response.status(404);
/// @endcode
////////////////////////////////////////////////////////////////////////////////

      status: function (code) {
        this.responseCode = code;
      },

////////////////////////////////////////////////////////////////////////////////
/// @fn JSF_foxx_BaseMiddleware_response_set
/// @brief The radical `set` function
///
/// @FUN{response.set(@FA{key}, @FA{value})}
///
/// Set a header attribute, for example:
///
/// *Examples*
///
/// @code
///     response.set("Content-Length", 123);
///     response.set("Content-Type", "text/plain");
///
///     // or alternatively:
///
///     response.set({
///       "Content-Length": "123",
///       "Content-Type": "text/plain"
///     });
/// @endcode
////////////////////////////////////////////////////////////////////////////////

      set: function (key, value) {
        var attributes = {};
        if (_.isUndefined(value)) {
          attributes = key;
        } else {
          attributes[key] = value;
        }

        _.each(attributes, function (value, key) {
          key = key.toLowerCase();
          this.headers = this.headers || {};
          this.headers[key] = value;

          if (key === "content-type") {
            this.contentType = value;
          }
        }, this);
      },

////////////////////////////////////////////////////////////////////////////////
/// @fn JSF_foxx_BaseMiddleware_response_json
/// @brief The magical `json` function
///
/// @FUN{response.json(@FA{object})}
///
/// Set the content type to JSON and the body to the JSON encoded @FA{object}
/// you provided.
///
/// *Examples*
///
/// @code
///     response.json({'born': 'December 12, 1915'});
/// @endcode
////////////////////////////////////////////////////////////////////////////////

      json: function (obj) {
        this.contentType = "application/json";
        this.body = JSON.stringify(obj);
      }
    };

////////////////////////////////////////////////////////////////////////////////
/// @fn JSF_foxx_BaseMiddleware_response_trace
/// @brief trace
////////////////////////////////////////////////////////////////////////////////

    trace = options.isDevelopment;
    if (!trace && options.hasOwnProperty("options")) {
      trace = options.options.trace;
    }

    if (trace) {
      console.log("%s, incoming request from %s: %s",
                  options.mount,
                  request.client.address,
                  actions.stringifyRequestAddress(request));
    }

    _.extend(request, requestFunctions);
    _.extend(response, responseFunctions);

    next();

    if (trace) {
      if (response.hasOwnProperty("body")) {
        var bodyLength = 0;
        if (response.body !== undefined) {
          if (typeof response.body === "string") {
            bodyLength = parseInt(response.body.length, 10);
          } else {
            try {
              bodyLength = String(response.body).length;
            } catch (err) {
              // ignore if this fails
            }
          }
        }
        console.log("%s, outgoing response with status %s of type %s, body length: %d",
                    options.mount,
                    response.responseCode,
                    response.contentType,
                    bodyLength);
      } else if (response.hasOwnProperty("bodyFromFile")) {
        console.log("%s, outgoing response with status %s of type %s, body file: %s",
                    options.mount,
                    response.responseCode,
                    response.contentType,
                    response.bodyFromFile);
      } else {
        console.log("%s, outgoing response with status %s of type %s, no body",
                    options.mount,
                    response.responseCode,
                    response.contentType);
      }
    }
  };

  return {
    stringRepresentation: String(middleware),
    functionRepresentation: middleware
  };
};

exports.BaseMiddleware = BaseMiddleware;

// -----------------------------------------------------------------------------
// --SECTION--                                                       END-OF-FILE
// -----------------------------------------------------------------------------

/// Local Variables:
/// mode: outline-minor
/// outline-regexp: "/// @brief\\|/// @addtogroup\\|/// @page\\|// --SECTION--\\|/// @\\}\\|/\\*jslint"
/// End:
<commit_msg>Foxx: Fix error for `body()` when body is empty<commit_after>/*jslint indent: 2, nomen: true, maxlen: 120, vars: true */
/*global module, require, exports */

////////////////////////////////////////////////////////////////////////////////
/// @brief Foxx BaseMiddleware
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2013 triagens GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
///     http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is triAGENS GmbH, Cologne, Germany
///
/// @author Lucas Dohmen
/// @author Copyright 2013, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////

var BaseMiddleware;

////////////////////////////////////////////////////////////////////////////////
/// @fn JSF_foxx_BaseMiddleware_initializer
/// @brief The Base Middleware
///
/// The `BaseMiddleware` manipulates the request and response
/// objects to give you a nicer API.
////////////////////////////////////////////////////////////////////////////////

BaseMiddleware = function () {
  'use strict';
  var middleware = function (request, response, options, next) {
    var responseFunctions,
      requestFunctions,
      trace,
      _ = require("underscore"),
      console = require("console"),
      actions = require("org/arangodb/actions");

    requestFunctions = {

////////////////////////////////////////////////////////////////////////////////
/// @fn JSF_foxx_BaseMiddleware_request_body
/// @brief Get the JSON parsed body of the request
///
/// @FUN{request.body()}
///
/// Get the JSON parsed body of the request. If you need the raw version, please
/// refer to the `rawBody` function.
////////////////////////////////////////////////////////////////////////////////

      body: function () {
        var requestBody = this.requestBody || '{}';
        return JSON.parse(requestBody);
      },

////////////////////////////////////////////////////////////////////////////////
/// @fn JSF_foxx_BaseMiddleware_request_rawBody
/// @brief Get the raw body of the request
///
/// @FUN{request.rawBody()}
///
/// The raw request body, not parsed. Just a String.
////////////////////////////////////////////////////////////////////////////////

      rawBody: function () {
        return this.requestBody;
      },

////////////////////////////////////////////////////////////////////////////////
/// @fn JSF_foxx_BaseMiddleware_request_params
/// @brief The jinxed `params` function
///
/// @FUN{request.params(@FA{key})}
///
/// Get the parameters of the request. This process is two-fold:
///
/// - If you have defined an URL like `/test/:id` and the user requested
///   `/test/1`, the call `params("id")` will return `1`.
/// - If you have defined an URL like `/test` and the user gives a query
///   component, the query parameters will also be returned.  So for example if
///   the user requested `/test?a=2`, the call `params("a")` will return `2`.
////////////////////////////////////////////////////////////////////////////////

      params: function (key) {
        var ps = {};
        _.extend(ps, this.urlParameters);
        _.extend(ps, this.parameters);
        return ps[key];
      }
    };

    responseFunctions = {

////////////////////////////////////////////////////////////////////////////////
/// @fn JSF_foxx_BaseMiddleware_response_status
/// @brief The straightforward `status` function
///
/// @FUN{response.status(@FA{code})}
///
/// Set the status @FA{code} of your response, for example:
///
/// *Examples*
///
/// @code
///    response.status(404);
/// @endcode
////////////////////////////////////////////////////////////////////////////////

      status: function (code) {
        this.responseCode = code;
      },

////////////////////////////////////////////////////////////////////////////////
/// @fn JSF_foxx_BaseMiddleware_response_set
/// @brief The radical `set` function
///
/// @FUN{response.set(@FA{key}, @FA{value})}
///
/// Set a header attribute, for example:
///
/// *Examples*
///
/// @code
///     response.set("Content-Length", 123);
///     response.set("Content-Type", "text/plain");
///
///     // or alternatively:
///
///     response.set({
///       "Content-Length": "123",
///       "Content-Type": "text/plain"
///     });
/// @endcode
////////////////////////////////////////////////////////////////////////////////

      set: function (key, value) {
        var attributes = {};
        if (_.isUndefined(value)) {
          attributes = key;
        } else {
          attributes[key] = value;
        }

        _.each(attributes, function (value, key) {
          key = key.toLowerCase();
          this.headers = this.headers || {};
          this.headers[key] = value;

          if (key === "content-type") {
            this.contentType = value;
          }
        }, this);
      },

////////////////////////////////////////////////////////////////////////////////
/// @fn JSF_foxx_BaseMiddleware_response_json
/// @brief The magical `json` function
///
/// @FUN{response.json(@FA{object})}
///
/// Set the content type to JSON and the body to the JSON encoded @FA{object}
/// you provided.
///
/// *Examples*
///
/// @code
///     response.json({'born': 'December 12, 1915'});
/// @endcode
////////////////////////////////////////////////////////////////////////////////

      json: function (obj) {
        this.contentType = "application/json";
        this.body = JSON.stringify(obj);
      }
    };

////////////////////////////////////////////////////////////////////////////////
/// @fn JSF_foxx_BaseMiddleware_response_trace
/// @brief trace
////////////////////////////////////////////////////////////////////////////////

    trace = options.isDevelopment;
    if (!trace && options.hasOwnProperty("options")) {
      trace = options.options.trace;
    }

    if (trace) {
      console.log("%s, incoming request from %s: %s",
                  options.mount,
                  request.client.address,
                  actions.stringifyRequestAddress(request));
    }

    _.extend(request, requestFunctions);
    _.extend(response, responseFunctions);

    next();

    if (trace) {
      if (response.hasOwnProperty("body")) {
        var bodyLength = 0;
        if (response.body !== undefined) {
          if (typeof response.body === "string") {
            bodyLength = parseInt(response.body.length, 10);
          } else {
            try {
              bodyLength = String(response.body).length;
            } catch (err) {
              // ignore if this fails
            }
          }
        }
        console.log("%s, outgoing response with status %s of type %s, body length: %d",
                    options.mount,
                    response.responseCode,
                    response.contentType,
                    bodyLength);
      } else if (response.hasOwnProperty("bodyFromFile")) {
        console.log("%s, outgoing response with status %s of type %s, body file: %s",
                    options.mount,
                    response.responseCode,
                    response.contentType,
                    response.bodyFromFile);
      } else {
        console.log("%s, outgoing response with status %s of type %s, no body",
                    options.mount,
                    response.responseCode,
                    response.contentType);
      }
    }
  };

  return {
    stringRepresentation: String(middleware),
    functionRepresentation: middleware
  };
};

exports.BaseMiddleware = BaseMiddleware;

// -----------------------------------------------------------------------------
// --SECTION--                                                       END-OF-FILE
// -----------------------------------------------------------------------------

/// Local Variables:
/// mode: outline-minor
/// outline-regexp: "/// @brief\\|/// @addtogroup\\|/// @page\\|// --SECTION--\\|/// @\\}\\|/\\*jslint"
/// End:
<|endoftext|>


<commit_before>/**
 * This file contains the marker positions and additional information for the map
 */
var app = app || {};

(function () {
    'use strict';

	app.markers = [
		// Mainhall
		{
			name: 'Megablast',
			coord: [-54, -100],
			desc: 'Music-Player for the main hall.',
			url: 'http://megablast.cbrp3.c-base.org/',
			icon: app.audio_icon
		},
		{
			name: 'DMX-DMON',
			coord: [-54, -113.0],
			desc: 'DMX light control for the main hall.',
			url: 'http://baseos.cbrp3.c-base.org/dmxdmon/',
			icon: app.lightbulb_icon
		},
		{
			name: 'Bar',
			coord: [-30.0, -112.0],
			desc: 'A place to get cold beverages.',
			url: '',
			icon: app.bar_icon
		},
		{
			name: 'Replicator',
			coord: [-37.0, -66.0],
			desc: 'A place to get cold beverages from a machine. it gives a discount if you provide a UID and PIN',
			url: 'https://member.cbrp3.c-base.org/account/gastropin/',
			icon: app.bar_icon
		},
		{
			name: 'Matelight',
			coord: [-69.0, -80.0],
			desc: 'Mate bottle based pixel display.',
			url: 'http://matelight.cbrp3.c-base.org/',
			icon: app.videogames_icon
		},
		{
			name: 'Klotür',
			coord: [5.0, -142.5],
			desc: 'The Twitter account of an object that caused a shitstorm.',
			url: 'https://twitter.com/klotuer',
			icon: app.twitter_icon
		},
		// C_leuse
		{
			name: 'c_leuse lightcontrol',
			coord: [-5.0, -35.0],
			desc: 'control of photon emitters in airlock',
			url: 'https://c-beam.cbrp3.c-base.org/c_leuse',
			icon: app.lightbulb_icon
		},
		{
			name: 'c_leuse soundcontrol',
			coord: [-5.0, -45.0],
			desc: 'no webinterface. only physical interface at the bar',
			url: 'https://jaseg.net/how-to-build-a-very-serious-volume-knob-from-a-1950ies-variac-and-an-arduino.html',
			icon: app.audio_icon
		},
		// Nerdarea
		{
			name: 'Hyperblast',
			coord: [-47.0, 6.0],
			desc: 'Music-Player in the Nördarea.',
			url: 'http://hyperblast.cbrp3.c-base.org/',
			icon: app.audio_icon
		},

		// c-lab
		{
			name: 'Labblaster',
			coord: [-55.0, 130.0],
			desc: 'Music player for the c-lab',
			url: 'http://labblaster.cbrp3.c-base.org/',
			icon: app.audio_icon
		},
		{
			name: '3D-Printer Cams',
			coord: [-30.0, 115.0],
			desc: 'Webcams and Webinterface of the 3D-Printers',
			url: 'http://3dp.cbrp3.c-base.org/',
			icon: app.video_icon
		},

		// werkstatt
		{
			name: 'Mechblast',
			coord: [30.0, 25.0],
			desc: 'Music player for the workshop',
			url: 'http://mechblast.cbrp3.c-base.org',
			icon: app.audio_icon
		},

	];

})();

<commit_msg>3d printer cams auskommentiert weil derzeit keine 3dprinter aktiv sind.<commit_after>/**
 * This file contains the marker positions and additional information for the map
 */
var app = app || {};

(function () {
    'use strict';

	app.markers = [
		// Mainhall
		{
			name: 'Megablast',
			coord: [-54, -100],
			desc: 'Music-Player for the main hall.',
			url: 'http://megablast.cbrp3.c-base.org/',
			icon: app.audio_icon
		},
		{
			name: 'DMX-DMON',
			coord: [-54, -113.0],
			desc: 'DMX light control for the main hall.',
			url: 'http://baseos.cbrp3.c-base.org/dmxdmon/',
			icon: app.lightbulb_icon
		},
		{
			name: 'Bar',
			coord: [-30.0, -112.0],
			desc: 'A place to get cold beverages.',
			url: '',
			icon: app.bar_icon
		},
		{
			name: 'Replicator',
			coord: [-37.0, -66.0],
			desc: 'A place to get cold beverages from a machine. it gives a discount if you provide a UID and PIN',
			url: 'https://member.cbrp3.c-base.org/account/gastropin/',
			icon: app.bar_icon
		},
		{
			name: 'Matelight',
			coord: [-69.0, -80.0],
			desc: 'Mate bottle based pixel display.',
			url: 'http://matelight.cbrp3.c-base.org/',
			icon: app.videogames_icon
		},
		{
			name: 'Klotür',
			coord: [5.0, -142.5],
			desc: 'The Twitter account of an object that caused a shitstorm.',
			url: 'https://twitter.com/klotuer',
			icon: app.twitter_icon
		},
		// C_leuse
		{
			name: 'c_leuse lightcontrol',
			coord: [-5.0, -35.0],
			desc: 'control of photon emitters in airlock',
			url: 'https://c-beam.cbrp3.c-base.org/c_leuse',
			icon: app.lightbulb_icon
		},
		{
			name: 'c_leuse soundcontrol',
			coord: [-5.0, -45.0],
			desc: 'no webinterface. only physical interface at the bar',
			url: 'https://jaseg.net/how-to-build-a-very-serious-volume-knob-from-a-1950ies-variac-and-an-arduino.html',
			icon: app.audio_icon
		},
		// Nerdarea
		{
			name: 'Hyperblast',
			coord: [-47.0, 6.0],
			desc: 'Music-Player in the Nördarea.',
			url: 'http://hyperblast.cbrp3.c-base.org/',
			icon: app.audio_icon
		},

		// c-lab
		{
			name: 'Labblaster',
			coord: [-55.0, 130.0],
			desc: 'Music player for the c-lab',
			url: 'http://labblaster.cbrp3.c-base.org/',
			icon: app.audio_icon
		},
	//	{
	//		name: '3D-Printer Cams',
	//		coord: [-30.0, 115.0],
	//		desc: 'Webcams and Webinterface of the 3D-Printers',
	//		url: 'http://3dp.cbrp3.c-base.org/',
	//		icon: app.video_icon
	//	},

		// werkstatt
		{
			name: 'Mechblast',
			coord: [30.0, 25.0],
			desc: 'Music player for the workshop',
			url: 'http://mechblast.cbrp3.c-base.org',
			icon: app.audio_icon
		},

	];

})();

<|endoftext|>


<commit_before><commit_msg>implementatio oauth<commit_after>var express =require('express'),
    bodyParser=require('body-parser'),
    oauthserver = require('node-oauth-server');

var app = express();

app.oauth = oauthserver({
  model: {}, // See below for specification
  grants: ['password'],
  debug: true
});

app.all('/oauth/token', app.oauth.grant());

app.get('/', app.oauth.authorise(), function (req, res) {
  res.send('Secret area');
});

app.use(app.oauth.errorHandler());

app.listen(5000);
<|endoftext|>


<commit_before>/*
 * Copyright 2011 eBay Software Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
'use strict';
require('raptor-polyfill/string/startsWith');
var isObjectEmpty = require('raptor-util/isObjectEmpty');
var fs = require('fs');
var nodePath = require('path');

var markoWidgets = require('../');

function getWidgetNode(node) {
    if (!node) {
        return;
    }

    while (true) {
        if (node.qName === 'w-widget') {
            return node;
        }

        node = node.parentNode;
        if (!node) {
            break;
        }
    }
}

function getDefaultWidgetModule(dirname) {
    if (fs.existsSync(nodePath.join(dirname, 'widget.js'))) {
        return './widget';
    } else if (fs.existsSync(nodePath.join(dirname, 'index.js'))) {
        return './';
    } else {
        return null;
    }
}

exports.process =function (node, compiler, template) {
    var props = node.getProperties();

    if (!node._ts) {
        node._ts = Math.random();
    }

    var widgetTypes = [];

    function registerType(target) {
        var typePathExpression;
        var targetExpression;

        if (compiler.hasExpression(target)) {
            return '__markoWidgets.getDynamicClientWidgetPath(' + compiler.convertType(target, 'string', true) + ')';
        }

        // Resolve the static string to a full path at compile time
        typePathExpression = template.addStaticVar(target === './' ? '__widgetPath' : target, JSON.stringify(markoWidgets.getClientWidgetPath(target, template.dirname)));
        targetExpression = 'require(' + JSON.stringify(target) + ')';

        widgetTypes.push({
            name: typePathExpression,
            target: targetExpression
        });

        template.addStaticCode(function(writer) {
            writer.line('if (typeof window != "undefined") {');
            writer.incIndent();
            widgetTypes.forEach(function(registeredType) {
                writer.line('__markoWidgets.registry.register(' + registeredType.name + ', ' + registeredType.target + ');');
            });

            writer.decIndent();
            writer.line('}');
        });

        return typePathExpression;
    }

    var bind;

    if ((bind = props['w-bind']) != null) {

        if (bind === '') {
            bind = getDefaultWidgetModule(template.dirname);
            if (!bind) {
                node.addError('Unable to find default widget module when using w-bind without a value');
                return;
            }
        }

        template.addStaticVar('__markoWidgets', 'require("marko-widgets")');

        // A widget is bound to the node
        var widgetAttrsVar = template.addStaticVar('_widgetAttrs', '__markoWidgets.attrs');

        var typePathExpression = registerType(bind);

        var config;
        var assignedId;
        var scope;
        var id;

        var widgetNode = compiler.createTagHandlerNode('w-widget');
        node.parentNode.replaceChild(widgetNode, node);
        widgetNode.appendChild(node);

        widgetNode.setAttribute('module', typePathExpression);

        if ((config = props['w-config'])) {
            widgetNode.setProperty('config', config);
        }

        if ((assignedId = props['w-assigned-id'])) {
            widgetNode.setProperty('assignedId', assignedId);
        }

        if ((scope = props['w-scope'])) {
            widgetNode.setProperty('scope', scope);
        }

        if ((id = node.getAttribute('id'))) {
            id = compiler.convertType(id, 'string', true);
            widgetNode.setProperty('id', id);
        }

        node.setAttribute('id', '${widget.elId()}');

        node.addDynamicAttributes(template.makeExpression(widgetAttrsVar + '(widget)'));
    } else {
        var widgetId;
        var widgetExtend;
        var widgetElIdExpression;
        var widgetFor;

        var widgetArgs = {};

        if ((widgetId = props['w-id'])) {
            // Handle the "w-id" attribute
            delete props['w-id'];
            widgetArgs.id = widgetId;
        } else if ((widgetExtend = props['w-extend']) != null) {
            if (widgetExtend === '') {
                widgetExtend = getDefaultWidgetModule(template.dirname);
                if (!widgetExtend) {
                    node.addError('Unable to find default widget module when using w-extend without a value');
                    return;
                }
            }

            // Handle the "w-extend" attribute
            delete props['w-extend'];
            template.addStaticVar('__markoWidgets', 'require("marko-widgets")');
            widgetArgs.extend = registerType(widgetExtend);
            widgetArgs.extendConfig = template.makeExpression('data.widgetConfig');
        } else if ((widgetElIdExpression = props['w-el-id'])) {
            // Handle the "w-el-id" attribute
            if (node.hasAttribute('id')) {
                node.addError('The "w-el-id" attribute cannot be used in conjuction with the "id" attribute');
            } else {
                node.setAttribute(
                    'id',
                    template.makeExpression('widget.elId(' +
                        widgetElIdExpression.toString() +
                        ')'));
            }
        } else if ((widgetFor = props['w-for'])) {
            // Handle the "w-for" attribute
            if (node.hasAttribute('for')) {
                node.addError('The "w-for" attribute cannot be used in conjuction with the "for" attribute');
            } else {
                node.setAttribute(
                    'for',
                    template.makeExpression('widget.elId(' +
                        compiler.convertType(widgetFor, 'string', true) +
                        ')'));
            }
        }

        if (!isObjectEmpty(widgetArgs)) {

            template.addStaticVar('_widgetArgs',
                'require("marko-widgets/taglib/helpers").widgetArgs');

            template.addStaticVar('_cleanupWidgetArgs',
                'require("marko-widgets/taglib/helpers").cleanupWidgetArgs');

            var widgetArgsParts = [];
            if (widgetArgs.id) {
                widgetArgsParts.push(JSON.stringify(widgetArgs.id.toString()));
                widgetArgsParts.push('widget');
            } else {
                widgetArgsParts.push('null');
                widgetArgsParts.push('null');
            }
            if (widgetArgs.events) {
                widgetArgsParts.push(widgetArgs.events);
            }

            if (widgetArgs.extend) {
                if (!widgetArgs.events) {
                    widgetArgsParts.push('null');
                }

                widgetArgsParts.push(widgetArgs.extend);
                widgetArgsParts.push(widgetArgs.extendConfig);
            }

            node.addBeforeCode(template.makeExpression('_widgetArgs(out,' + widgetArgsParts.join(', ') + ');'));
            node.addAfterCode(template.makeExpression('_cleanupWidgetArgs(out);'));
        }
    }

    var widgetTagNode;
    var eventIdExpression;

    function addDirectEventListener(eventType, targetMethod) {
        // The event does not support bubbling, so the widget
        // must attach the listeners directly to the target
        // elements when the widget is initialized.
        if (!widgetTagNode) {
            widgetTagNode = getWidgetNode(node);
        }

        if (!widgetTagNode) {
            node.addError('Unable to handle event "' + eventType + '". HTML element is not nested within a widget.');
            return;
        }

        if (!eventIdExpression) {
            // In order to attach a DOM event listener directly we need to make sure
            // the target HTML element has an ID that we can use to get a reference
            // to the element during initialization. We need to handle the following
            // scenarios:
            //
            // 1) The HTML element already has an "id" attribute
            // 2) The HTML element has a "w-el-id" attribute (we already converted this
            //    to an "id" attribute above)
            // 3) The HTML does not have an "id" or "w-el-id" attribute. We must add
            //    an "id" attribute with a unique ID.

            var idAttr = node.getAttribute('id');
            if (idAttr) {
                // Case 1 and 2 -- Using the existing "id" attribute
                // The "id" attribute can be a JavaScript expression or a raw String
                // value. We need a JavaScript expression that can be used to
                // provide the same ID at runtime.

                if (bind) {
                    // We have to attach a listener to the root element of the widget
                    // We will use "!" as an indicator that it is the root widget
                    // element.
                    //
                    // Use an exclamation point to make it clear that we
                    // we need to resolve the ID as a root widget element ID.
                    eventIdExpression = compiler.makeExpression('"!"');
                } else if (widgetElIdExpression) {
                    // We have to attach a listener to a nested HTML element of the widget
                    // that was assigned an ID using "w-el-id".
                    //
                    // Prefix widget element ID with an exclamation point to make it clear that we
                    // we need to resolve the ID as a widget element ID.
                    eventIdExpression = compiler.makeExpression('"!"+' + widgetElIdExpression.toString());
                } else if (typeof idAttr === 'string') {
                    // Convert the raw String to a JavaScript expression
                    eventIdExpression = compiler.convertType(idAttr, 'string', true);
                } else {
                    // The "id" attribute is already expression that we can use that
                    // directly
                    eventIdExpression = idAttr;
                }
            } else {
                // Case 3 - We need to add a unique "id" attribute

                // We'll add a property to keep track of our next widget ID
                // NOTE: This is at compile time and "template.data" is only
                //       used for the current template compilation. We need
                //       a unique ID that
                if (template.data.widgetNextId == null) {
                    template.data.widgetNextId = 0;
                }

                var uniqueId = '_' + (template.data.widgetNextId++);

                // Prefix the unique ID with an exclamation point to make it clear that we
                // we need to resolve the ID as a widget element ID.
                eventIdExpression = compiler.makeExpression(JSON.stringify('!' + uniqueId));

                node.setAttribute('id', compiler.makeExpression('widget.elId("' +
                    uniqueId +
                    '")'));
            }
        }

        if (!widgetTagNode.data.widgetEvents) {
            // Add a new input property to the widget tag that will contain
            // enough information to allow the DOM event listeners to
            // be attached directly to the DOM elements.
            widgetTagNode.data.widgetEvents = [];
            widgetTagNode.setProperty('events', function() {
                return compiler.makeExpression(
                    '[' + widgetTagNode.data.widgetEvents.join(',') + ']');
            });
        }

        // Add a 3-tuple consisting of <event-type><target-method><DOM element ID>
        widgetTagNode.data.widgetEvents.push(JSON.stringify(eventType));
        widgetTagNode.data.widgetEvents.push(JSON.stringify(targetMethod));
        widgetTagNode.data.widgetEvents.push(eventIdExpression.toString());
    }

    function addBubblingEventListener(eventType, targetMethod) {

        if (!widgetTagNode) {
            widgetTagNode = getWidgetNode(node);
        }

        if (!widgetTagNode) {
            node.addError('Unable to handle event "' + eventType + '". HTML element is not nested within a widget.');
            return;
        }

        node.setAttribute('data-' + propName,
            compiler.makeExpression(JSON.stringify(targetMethod + '|') +
            '+widget.id'));
    }

    if (node.hasFlag('hasWidgetEvents')) {
        // The Marko compiler was nice enough to attach a flag to nodes that
        // have one or more attributes that match the "w-on*" pattern.
        // We still need to loop over the properties to find and handle
        // the properties corresponding to those attributes
        for (var propName in props) {
            if (props.hasOwnProperty(propName) && propName.startsWith('w-on')) {

                propName = propName.toLowerCase();

                var eventType = propName.substring(4); // Chop off "w-on"

                var targetMethod = props[propName];

                var isBubbleEvent = markoWidgets.isBubbleEvent(eventType);

                if (isBubbleEvent) {
                    // The event is white listed for bubbling so we know that
                    // we have already attached a listener on document.body
                    // that can be used to handle the event. We will add
                    // a "data-w-on{eventType}" attribute to the output HTML
                    // for this element that will be used to map the event
                    // to a method on the containing widget.
                    addBubblingEventListener(eventType, targetMethod);
                } else {
                    // The event does not bubble so we must attach a DOM
                    // event listener directly to the target element.
                    addDirectEventListener(eventType, targetMethod);
                }
            }
        }
    }

    if (node.qName === 'w-widget') {
        if (node.getAttribute('id') != null) {
            node.setProperty('scope', template.makeExpression('widget'));
        }
    }
};<commit_msg>Fixes #17 Allow w-config with w-extend<commit_after>/*
 * Copyright 2011 eBay Software Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
'use strict';
require('raptor-polyfill/string/startsWith');
var isObjectEmpty = require('raptor-util/isObjectEmpty');
var fs = require('fs');
var nodePath = require('path');

var markoWidgets = require('../');

function getWidgetNode(node) {
    if (!node) {
        return;
    }

    while (true) {
        if (node.qName === 'w-widget') {
            return node;
        }

        node = node.parentNode;
        if (!node) {
            break;
        }
    }
}

function getDefaultWidgetModule(dirname) {
    if (fs.existsSync(nodePath.join(dirname, 'widget.js'))) {
        return './widget';
    } else if (fs.existsSync(nodePath.join(dirname, 'index.js'))) {
        return './';
    } else {
        return null;
    }
}

exports.process =function (node, compiler, template) {
    var props = node.getProperties();

    if (!node._ts) {
        node._ts = Math.random();
    }

    var widgetTypes = [];

    function registerType(target) {
        var typePathExpression;
        var targetExpression;

        if (compiler.hasExpression(target)) {
            return '__markoWidgets.getDynamicClientWidgetPath(' + compiler.convertType(target, 'string', true) + ')';
        }

        // Resolve the static string to a full path at compile time
        typePathExpression = template.addStaticVar(target === './' ? '__widgetPath' : target, JSON.stringify(markoWidgets.getClientWidgetPath(target, template.dirname)));
        targetExpression = 'require(' + JSON.stringify(target) + ')';

        widgetTypes.push({
            name: typePathExpression,
            target: targetExpression
        });

        template.addStaticCode(function(writer) {
            writer.line('if (typeof window != "undefined") {');
            writer.incIndent();
            widgetTypes.forEach(function(registeredType) {
                writer.line('__markoWidgets.registry.register(' + registeredType.name + ', ' + registeredType.target + ');');
            });

            writer.decIndent();
            writer.line('}');
        });

        return typePathExpression;
    }

    var bind;

    if ((bind = props['w-bind']) != null) {

        if (bind === '') {
            bind = getDefaultWidgetModule(template.dirname);
            if (!bind) {
                node.addError('Unable to find default widget module when using w-bind without a value');
                return;
            }
        }

        template.addStaticVar('__markoWidgets', 'require("marko-widgets")');

        // A widget is bound to the node
        var widgetAttrsVar = template.addStaticVar('_widgetAttrs', '__markoWidgets.attrs');

        var typePathExpression = registerType(bind);

        var config;
        var assignedId;
        var scope;
        var id;

        var widgetNode = compiler.createTagHandlerNode('w-widget');
        node.parentNode.replaceChild(widgetNode, node);
        widgetNode.appendChild(node);

        widgetNode.setAttribute('module', typePathExpression);

        if ((config = props['w-config'])) {
            widgetNode.setProperty('config', config);
        }

        if ((assignedId = props['w-assigned-id'])) {
            widgetNode.setProperty('assignedId', assignedId);
        }

        if ((scope = props['w-scope'])) {
            widgetNode.setProperty('scope', scope);
        }

        if ((id = node.getAttribute('id'))) {
            id = compiler.convertType(id, 'string', true);
            widgetNode.setProperty('id', id);
        }

        node.setAttribute('id', '${widget.elId()}');

        node.addDynamicAttributes(template.makeExpression(widgetAttrsVar + '(widget)'));
    } else {
        var widgetId;
        var widgetExtend;
        var widgetElIdExpression;
        var widgetFor;

        var widgetArgs = {};

        if ((widgetId = props['w-id'])) {
            // Handle the "w-id" attribute
            delete props['w-id'];
            widgetArgs.id = widgetId;
        } else if ((widgetExtend = props['w-extend']) != null) {
            if (widgetExtend === '') {
                widgetExtend = getDefaultWidgetModule(template.dirname);
                if (!widgetExtend) {
                    node.addError('Unable to find default widget module when using w-extend without a value');
                    return;
                }
            }

            // Handle the "w-extend" attribute
            delete props['w-extend'];
            template.addStaticVar('__markoWidgets', 'require("marko-widgets")');
            widgetArgs.extend = registerType(widgetExtend);

            var extendConfig = props['w-config'];

            if (extendConfig) {
                widgetArgs.extendConfig = template.makeExpression(extendConfig);
            } else {
                widgetArgs.extendConfig = template.makeExpression('data.widgetConfig');
            }
        } else if ((widgetElIdExpression = props['w-el-id'])) {
            // Handle the "w-el-id" attribute
            if (node.hasAttribute('id')) {
                node.addError('The "w-el-id" attribute cannot be used in conjuction with the "id" attribute');
            } else {
                node.setAttribute(
                    'id',
                    template.makeExpression('widget.elId(' +
                        widgetElIdExpression.toString() +
                        ')'));
            }
        } else if ((widgetFor = props['w-for'])) {
            // Handle the "w-for" attribute
            if (node.hasAttribute('for')) {
                node.addError('The "w-for" attribute cannot be used in conjuction with the "for" attribute');
            } else {
                node.setAttribute(
                    'for',
                    template.makeExpression('widget.elId(' +
                        compiler.convertType(widgetFor, 'string', true) +
                        ')'));
            }
        }

        if (!isObjectEmpty(widgetArgs)) {

            template.addStaticVar('_widgetArgs',
                'require("marko-widgets/taglib/helpers").widgetArgs');

            template.addStaticVar('_cleanupWidgetArgs',
                'require("marko-widgets/taglib/helpers").cleanupWidgetArgs');

            var widgetArgsParts = [];
            if (widgetArgs.id) {
                widgetArgsParts.push(JSON.stringify(widgetArgs.id.toString()));
                widgetArgsParts.push('widget');
            } else {
                widgetArgsParts.push('null');
                widgetArgsParts.push('null');
            }
            if (widgetArgs.events) {
                widgetArgsParts.push(widgetArgs.events);
            }

            if (widgetArgs.extend) {
                if (!widgetArgs.events) {
                    widgetArgsParts.push('null');
                }

                widgetArgsParts.push(widgetArgs.extend);
                widgetArgsParts.push(widgetArgs.extendConfig);
            }

            node.addBeforeCode(template.makeExpression('_widgetArgs(out,' + widgetArgsParts.join(', ') + ');'));
            node.addAfterCode(template.makeExpression('_cleanupWidgetArgs(out);'));
        }
    }

    var widgetTagNode;
    var eventIdExpression;

    function addDirectEventListener(eventType, targetMethod) {
        // The event does not support bubbling, so the widget
        // must attach the listeners directly to the target
        // elements when the widget is initialized.
        if (!widgetTagNode) {
            widgetTagNode = getWidgetNode(node);
        }

        if (!widgetTagNode) {
            node.addError('Unable to handle event "' + eventType + '". HTML element is not nested within a widget.');
            return;
        }

        if (!eventIdExpression) {
            // In order to attach a DOM event listener directly we need to make sure
            // the target HTML element has an ID that we can use to get a reference
            // to the element during initialization. We need to handle the following
            // scenarios:
            //
            // 1) The HTML element already has an "id" attribute
            // 2) The HTML element has a "w-el-id" attribute (we already converted this
            //    to an "id" attribute above)
            // 3) The HTML does not have an "id" or "w-el-id" attribute. We must add
            //    an "id" attribute with a unique ID.

            var idAttr = node.getAttribute('id');
            if (idAttr) {
                // Case 1 and 2 -- Using the existing "id" attribute
                // The "id" attribute can be a JavaScript expression or a raw String
                // value. We need a JavaScript expression that can be used to
                // provide the same ID at runtime.

                if (bind) {
                    // We have to attach a listener to the root element of the widget
                    // We will use "!" as an indicator that it is the root widget
                    // element.
                    //
                    // Use an exclamation point to make it clear that we
                    // we need to resolve the ID as a root widget element ID.
                    eventIdExpression = compiler.makeExpression('"!"');
                } else if (widgetElIdExpression) {
                    // We have to attach a listener to a nested HTML element of the widget
                    // that was assigned an ID using "w-el-id".
                    //
                    // Prefix widget element ID with an exclamation point to make it clear that we
                    // we need to resolve the ID as a widget element ID.
                    eventIdExpression = compiler.makeExpression('"!"+' + widgetElIdExpression.toString());
                } else if (typeof idAttr === 'string') {
                    // Convert the raw String to a JavaScript expression
                    eventIdExpression = compiler.convertType(idAttr, 'string', true);
                } else {
                    // The "id" attribute is already expression that we can use that
                    // directly
                    eventIdExpression = idAttr;
                }
            } else {
                // Case 3 - We need to add a unique "id" attribute

                // We'll add a property to keep track of our next widget ID
                // NOTE: This is at compile time and "template.data" is only
                //       used for the current template compilation. We need
                //       a unique ID that
                if (template.data.widgetNextId == null) {
                    template.data.widgetNextId = 0;
                }

                var uniqueId = '_' + (template.data.widgetNextId++);

                // Prefix the unique ID with an exclamation point to make it clear that we
                // we need to resolve the ID as a widget element ID.
                eventIdExpression = compiler.makeExpression(JSON.stringify('!' + uniqueId));

                node.setAttribute('id', compiler.makeExpression('widget.elId("' +
                    uniqueId +
                    '")'));
            }
        }

        if (!widgetTagNode.data.widgetEvents) {
            // Add a new input property to the widget tag that will contain
            // enough information to allow the DOM event listeners to
            // be attached directly to the DOM elements.
            widgetTagNode.data.widgetEvents = [];
            widgetTagNode.setProperty('events', function() {
                return compiler.makeExpression(
                    '[' + widgetTagNode.data.widgetEvents.join(',') + ']');
            });
        }

        // Add a 3-tuple consisting of <event-type><target-method><DOM element ID>
        widgetTagNode.data.widgetEvents.push(JSON.stringify(eventType));
        widgetTagNode.data.widgetEvents.push(JSON.stringify(targetMethod));
        widgetTagNode.data.widgetEvents.push(eventIdExpression.toString());
    }

    function addBubblingEventListener(eventType, targetMethod) {

        if (!widgetTagNode) {
            widgetTagNode = getWidgetNode(node);
        }

        if (!widgetTagNode) {
            node.addError('Unable to handle event "' + eventType + '". HTML element is not nested within a widget.');
            return;
        }

        node.setAttribute('data-' + propName,
            compiler.makeExpression(JSON.stringify(targetMethod + '|') +
            '+widget.id'));
    }

    if (node.hasFlag('hasWidgetEvents')) {
        // The Marko compiler was nice enough to attach a flag to nodes that
        // have one or more attributes that match the "w-on*" pattern.
        // We still need to loop over the properties to find and handle
        // the properties corresponding to those attributes
        for (var propName in props) {
            if (props.hasOwnProperty(propName) && propName.startsWith('w-on')) {

                propName = propName.toLowerCase();

                var eventType = propName.substring(4); // Chop off "w-on"

                var targetMethod = props[propName];

                var isBubbleEvent = markoWidgets.isBubbleEvent(eventType);

                if (isBubbleEvent) {
                    // The event is white listed for bubbling so we know that
                    // we have already attached a listener on document.body
                    // that can be used to handle the event. We will add
                    // a "data-w-on{eventType}" attribute to the output HTML
                    // for this element that will be used to map the event
                    // to a method on the containing widget.
                    addBubblingEventListener(eventType, targetMethod);
                } else {
                    // The event does not bubble so we must attach a DOM
                    // event listener directly to the target element.
                    addDirectEventListener(eventType, targetMethod);
                }
            }
        }
    }

    if (node.qName === 'w-widget') {
        if (node.getAttribute('id') != null) {
            node.setProperty('scope', template.makeExpression('widget'));
        }
    }
};<|endoftext|>


<commit_before>(function() {

  /**
   *  String prototype extend to represent property table size
   */
  String.prototype.bytesToSize = function() {
    var bytes = parseInt(this.toString())
      , sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
    if (bytes == 0) return '0 KB';
    var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
    var value = (bytes / Math.pow(1024, i)).toFixed(2);

    if (value % 1 == 0) {
      value = parseInt(value)
    }

    return value + " " + sizes[i];
  }

  /**
   * Single table item in dashboard list
   *
   * Usage example:
   *
      var li = new TableView({
        model: model*,
        limitation: !this.options.user.get("private_tables")
      });

      * It needs a table model to run correctly.
   *
   */
  var TableView = cdb.core.View.extend({

    tagName: 'li',

    events: {
      "click a.status": "_addPrivacySelector",
      "click a.delete": "_showDeleteConfirmation",
      "removeTag .tags a": "removeTag"
    },

    initialize: function() {
      _.bindAll(this, "render", "deleting", "deleted", "_addPrivacySelector", "removeTag", "_showDeleteConfirmation");

      _.defaults(this.options, this.default_options);

      this.template = cdb.templates.getTemplate('dashboard/views/table_list_item');

      // this.model.bind('destroy', this.clean, this);
      // this.model.bind('change', this.render, this);

      this.retrigger('saving', this.model);
      this.retrigger('saved', this.model);

      this.model.bind('updated', this.render);
      this.bind("clean", this._reClean, this);
    },

    render: function() {
      var self = this;
      this.cleanTooltips();
      this.$el.html(this.template(this.model.toJSON()));
      this.$el.addClass('tableRow')
      this.$el.droppable({
        hoverClass: "drop",
        drop: function( ev, ui ) {
          var tag = $(ui.helper).text()
            , tags = self.model.get("tags").split(",")
            , included = false;

          if (tags.indexOf(tag) < 0) {
            $.when(self.model.save({tags: tags + "," + tag})).done(function() {
              self.render();
              self.model.trigger('tagAdded');
            });
          }
          self.$el.removeClass('alreadyContainsTag').find('.right.tags a').removeClass('exists');
        },
        over: function(ev, ui) {
          var tag = $(ui.helper).text();
          self.$('.right.tags a').map(function(i,e) {
            var $element = $(e);
            if($element.html() == tag) {
              alreadyInserted = true;
              $element.addClass('exists');
              self.$el.addClass('alreadyContainsTag');
            }
          });
        },
        out: function() {
          self.$el.removeClass('alreadyContainsTag').find('.right.tags a').removeClass('exists');
        }
      });

      this.assignDraggables();
      return this;
    },
    /**
     * Hides the content and show a notification saying the table is being deleted
     * @triggers deleting
     * @return undefined
     */
    deleting: function() {
      this.cleanTooltips();
      this.trigger('deleting', this.model);
      this.$el.addClass('disabled');
    },

    /**
     * Close the "deleting" notification and warns the user that the table has been deleted
     * @triggers deleted
     * @return undefined
     */
    deleted: function() {
      this.trigger('deleted');
      this.cleanTooltips();
      this.$el.html('');

      var notificationTpl =
        '
Your table (' + this.model.get("name") + ') has been deleted
' +
        'x';
      var $container = $('

');
      this.$el.after($container);
      this.notification = new cdb.ui.common.Notification({
        el: $container,
        timeout:3000,
        template: notificationTpl,
        hideMethod: 'fadeOut',
        showMethod: 'fadeIn'
      });

      this.notification.open();
      this.$el.remove();

    },

    assignDraggables: function() {
        this.$('span.tags a').draggable({
        zIndex:9999,
        opacity: 1,
        helper: function(ev) {
          return $( "" + $(ev.currentTarget).html() + "" );
        },
        start: function() {
          $('.removeTags').animate({opacity:1, height:"18px", padding:"10px 10px 10px 10px", borderWidth:"1px"},50);
        },
        stop: function() {
          $('.removeTags').animate({opacity:0, height:"0px", padding:"0px 0px 0px 10px", borderWidth:0},50);
        }
      });
    },

    removeTag: function(ev) {
      var tag = $(ev.currentTarget).html();
      var tags = this.model.get("tags").split(",");
      var position = tags.indexOf(tag);
      if(position > -1) {
        tags.splice(position, 1)
      }
      this.model.save({tags: tags.join(',')});
      this.model.trigger('tagRemoved');
      // this.trigger('tagRemoved');
      this.model.trigger('updated');

    },

    clean: function() {
      this.trigger('clean');
      this.elder('clean');
    },

    /**
     * Create the privacy selector after a "privacy link" clicked
     */
    _addPrivacySelector: function(ev) {
      ev && (ev.preventDefault());
      this.trigger('removePrivacy');

      this.privacy && this.privacy.clean();

      // this.options.limitation = true
      // Add privacy selector
      var privacy = this.privacy = new cdb.admin.PrivacySelector({
        model: this.model,
        limitation: this.options.limitation,
        upgrade_url: '/account/'+username+'/upgrade'
        // isn't any other way to catch the username than from a global?
      });

      cdb.god.bind("closeDialogs", this.privacy.hide, this.privacy);

      this.$el.append(this.privacy.render().el);

      this.privacy.show(ev.target);

      return false;
    },

    /**
     * Show delete confirmation after decides delete a table
     */
    _showDeleteConfirmation: function(ev) {
      var self = this;
      ev && (ev.preventDefault());
      this.trigger('deleteDialog');
      this.delete_dialog = new cdb.admin.DeleteDialog({
        model: this.model,
        config: this.options.config,
        ok: function() {
          self.deleted();
        }
      });
      this.delete_dialog.bind('deleting', function() { })
      this.delete_dialog.bind('deleted', function() { })
      this.retrigger('deleted', this.delete_dialog);

      $("body").append(this.delete_dialog.render().el);
      this.delete_dialog.open();
      this.delete_dialog.bind('deleting', self.deleting)
    },

    /**
     * Destroy droppable funcionality when el is being cleaned
     */
    _reClean: function() {
      this.clean();
      this.$el.droppable("destroy");
    }
  });

  /**
   * Tables list in the dashboard
   *
   * It will show up the user tables in a list
   *
   * Usage example:
   *
      var tableList = new cdb.admin.dashboard.TableList({
        el: this.$('#tablelist'),
        model: this.tables*,
        user: this.user**   // it needs it to know if the user has limitations or not
      });

      *   It needs a tables model to run correctly.
      **  It needs a user model to work properly.
   */

  var TableList = cdb.core.View.extend({
    tagName: 'ul',
    _TABLES_PER_PAGE: 10,
    events: {
      "click li:not('.disabled') a.delete": "markForDeletion"
    },

    initialize: function() {
      window.tl =  this;
      _.bindAll(this, "render", "appendTableByNumber", "_showLoader", "_hideLoader", "hardRender", "_updateListHeader", "_addAll", "_addTable", "_removeAllPrivacy", "markForDeletion");
      _.defaults(this.options, this.default_options);

      this.bindEvents();
    },

    bindEvents: function() {
      this.model.bind('reset',    this._addAll, this);
      this.model.bind('forceReload', this.hardRender, this);
      this.model.bind('reset',  this._updateListHeader, this);
      this.model.bind('updated',  this._updateListHeader, this);
      this.model.bind('add',  this._updateListHeader, this);

      this.model.bind('updating', this._showLoader, this)
      this.model.bind('reset',  this._hideLoader, this);
      this.model.bind('updated',  this._hideLoader, this);
      this.model.bind('add',  this._hideLoader, this);

      this.model.bind('elementAdded', this.appendTableByNumber, this);
    },

    hardRender: function() {
      this.lastMarkedForDeletion = undefined;
      return this.render();
    },

    render: function() {
      var self = this;
      this.$el.html('');
      this._updateListHeader();
      if (this.model.length > 0) {
        this.model.each(function(m, i) {
          // if we are on the same position that the deleted table was, we insert the notification
          if(self.lastMarkedForDeletion) {
            if(self.lastMarkedForDeletion.pos == i) {
              self.showDeletedNotification();
            }
          }
          self._addTable(m);
          // each time a tag is removed, we forward the event to be able to refresh the tag list
          m.bind('tagRemoved', function() {
             self.model.trigger('tagRemoved');
             self.model.trigger('reset');
          });
          m.bind('tagAdded', function() {
             self.model.trigger('tagAdded');
             self.model.trigger('reset');
          });
          m.bind('deleted', function() {
            self.model.trigger('updated');
            self.model.trigger('reset');
          })

        });
        // if the lastMarkedForDeletion element was the last from the list, we shoudl add the notification at the end
        if(self.lastMarkedForDeletion && this.model.length <= self.lastMarkedForDeletion.pos) {
          this.showDeletedNotification();
        }
      } else {
        this._addEmpty();
      }
      this.trigger('renderComplete');
    },

    showDeletedNotification: function() {
      var self = this;
      if(this.lastMarkedForDeletion) {
        this.$('.notificationContainer').remove();

        var notificationTpl =
          '
Your table (' + this.lastMarkedForDeletion.name + ') has been deleted
' +
          'x';

        var $container = $('

');
        this.$el.append($container);
        if(this.notification && this.notification.destroy) {
          this.notification.removeData().unbind().remove().clean();
          delete this.notification;
        }
        this.notification = new cdb.ui.common.Notification({
          el: $container,
          timeout:10000,
          template: notificationTpl,
          hideMethod: 'fadeOut',
        });
        this.notification.unbind('notificationDeleted');
        this.notification.bind('notificationDeleted', function() {
          self.notificationShowing = false;
        })
        if(self.notificationShowing) {
          self.notification.open();
          self.notificationShowing = true;
        } else {
          self.notification.open('', function() {
            self.notificationShowing = true;
          });
        }

      }
    },

    _addEmpty: function() {
      this.$el.append(cdb.templates.getTemplate('dashboard/views/table_list_empty'))
    },

    /**
     * Add all list
     */
    _addAll: function() {

      if(!this.modelLoaded) {
        this.modelLoaded = true;
        this.render();
      } else {
        if(this.model.length == 0) {
          this.render();
          this._hideLoader();
        }
      }
    },

    /**
     * Add single table view
     */
    _addTable: function(m) {
      var self = this;
      var li = new TableView({ model: m, config: this.options.config, limitation: !this.options.user.get("private_tables") });
      li.bind('removePrivacy', this._removeAllPrivacy);
      this.$el.append(li.render().el);
      this.addView(li);
      this._updateListHeader();
      li.bind('saving', function() {
        self.model.trigger('updating');
      });
      li.bind('saved', function() {
        self.model.trigger('updated')
      })
      li.bind('deleted', function() {
        self.model.trigger('updated')
        self.model.trigger('reset');
        self.refreshTable();
      })
      li.bind('deleting', function(model) {
        self.model.trigger('updating');
        self.model.remove(model);
      })
    },

    /**
     * Checks if the table is uncompensated (has less tan _TABLES_PER_PAGE entries but has a next page)
     * and if needed, fills the gap
     */
    refreshTable: function() {
      var self = this;
      if(!this.checkTableListFull()) {
        this.model.refillTableList(this._TABLES_PER_PAGE);
      }
    },

    /**
     * Extract the n table from model and append it to the view
     * @param  {Integer} n
     */
    appendTableByNumber: function(n) {
      if(this.model.models[n]) {
        this._addTable(this.model.models[n]);
      }
    },

    /**
     * Checks if there are more less than ten tables loaded (because a deletion)
     * and that there are more tables after the current pages.
     * @return {[type]} [description]
     */
    checkTableListFull: function() {
      if(this.model.models.length < this._TABLES_PER_PAGE &&
        this.model.total_entries >= this._TABLES_PER_PAGE) {
          return false;
      }
      return true;
    },

    _removeAllPrivacy: function() {
      for(var v in this._subviews) {
        this._subviews[v].privacy && this._subviews[v].privacy.clean();
      }
    },

    markForDeletion: function(ev) {
      ev.preventDefault();
      ev.stopPropagation();

      var tableInfo = $(ev.currentTarget).parents('.tableRow');

      var tableIndex = this.$('.tableRow').index(tableInfo)
      this.lastMarkedForDeletion = {
        "pos": tableIndex,
        "name": this.$('.tableRow h3 a').html(),
        "li": tableInfo
      };

      self.notificationShowing = false;
      if(this.notification) {
        this.notification.hide()
      }
    },

    /**
     * After a table removed
     */
    _tableRemoved: function() {
      this._updateListHeader();
    },

    /**
     * Update the counter of tables
     */
    _updateListHeader: function(sync) {
      var title =  this.model.total_entries + " " + ( this.model.total_entries != 1 ? "tables" : "table" );

      if (this.model.options.attributes.tag_name != "")
        title += " with tag " + this.model.options.attributes.tag_name + "";

      if (this.model.options.attributes.q != "")
        title += " with " + this.model.options.attributes.q +  " found";

      if (this.model.options.attributes.q == "" && this.model.options.attributes.tag_name == "")
        title += " in your account";

      $("section.tables > div.head > h2").html(title);
    },

    /**
     * Show the loader when the table model is operating
     */
    _showLoader: function() {
      $("section.tables > div.head > div.loader").fadeIn();
    },

    /**
     * Hide the loader when the table model is operating
     */
    _hideLoader: function() {
      $("section.tables > div.head > div.loader").fadeOut();
    }
  });

  cdb.admin.dashboard.TableList = TableList;
})();
<commit_msg>tag with spaces now decoded<commit_after>(function() {

  /**
   *  String prototype extend to represent property table size
   */
  String.prototype.bytesToSize = function() {
    var bytes = parseInt(this.toString())
      , sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
    if (bytes == 0) return '0 KB';
    var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
    var value = (bytes / Math.pow(1024, i)).toFixed(2);

    if (value % 1 == 0) {
      value = parseInt(value)
    }

    return value + " " + sizes[i];
  }

  /**
   * Single table item in dashboard list
   *
   * Usage example:
   *
      var li = new TableView({
        model: model*,
        limitation: !this.options.user.get("private_tables")
      });

      * It needs a table model to run correctly.
   *
   */
  var TableView = cdb.core.View.extend({

    tagName: 'li',

    events: {
      "click a.status": "_addPrivacySelector",
      "click a.delete": "_showDeleteConfirmation",
      "removeTag .tags a": "removeTag"
    },

    initialize: function() {
      _.bindAll(this, "render", "deleting", "deleted", "_addPrivacySelector", "removeTag", "_showDeleteConfirmation");

      _.defaults(this.options, this.default_options);

      this.template = cdb.templates.getTemplate('dashboard/views/table_list_item');

      // this.model.bind('destroy', this.clean, this);
      // this.model.bind('change', this.render, this);

      this.retrigger('saving', this.model);
      this.retrigger('saved', this.model);

      this.model.bind('updated', this.render);
      this.bind("clean", this._reClean, this);
    },

    render: function() {
      var self = this;
      this.cleanTooltips();
      this.$el.html(this.template(this.model.toJSON()));
      this.$el.addClass('tableRow')
      this.$el.droppable({
        hoverClass: "drop",
        drop: function( ev, ui ) {
          var tag = $(ui.helper).text()
            , tags = self.model.get("tags").split(",")
            , included = false;

          if (tags.indexOf(tag) < 0) {
            $.when(self.model.save({tags: tags + "," + tag})).done(function() {
              self.render();
              self.model.trigger('tagAdded');
            });
          }
          self.$el.removeClass('alreadyContainsTag').find('.right.tags a').removeClass('exists');
        },
        over: function(ev, ui) {
          var tag = $(ui.helper).text();
          self.$('.right.tags a').map(function(i,e) {
            var $element = $(e);
            if($element.html() == tag) {
              alreadyInserted = true;
              $element.addClass('exists');
              self.$el.addClass('alreadyContainsTag');
            }
          });
        },
        out: function() {
          self.$el.removeClass('alreadyContainsTag').find('.right.tags a').removeClass('exists');
        }
      });

      this.assignDraggables();
      return this;
    },
    /**
     * Hides the content and show a notification saying the table is being deleted
     * @triggers deleting
     * @return undefined
     */
    deleting: function() {
      this.cleanTooltips();
      this.trigger('deleting', this.model);
      this.$el.addClass('disabled');
    },

    /**
     * Close the "deleting" notification and warns the user that the table has been deleted
     * @triggers deleted
     * @return undefined
     */
    deleted: function() {
      this.trigger('deleted');
      this.cleanTooltips();
      this.$el.html('');

      var notificationTpl =
        '
Your table (' + this.model.get("name") + ') has been deleted
' +
        'x';
      var $container = $('

');
      this.$el.after($container);
      this.notification = new cdb.ui.common.Notification({
        el: $container,
        timeout:3000,
        template: notificationTpl,
        hideMethod: 'fadeOut',
        showMethod: 'fadeIn'
      });

      this.notification.open();
      this.$el.remove();

    },

    assignDraggables: function() {
        this.$('span.tags a').draggable({
        zIndex:9999,
        opacity: 1,
        helper: function(ev) {
          return $( "" + $(ev.currentTarget).html() + "" );
        },
        start: function() {
          $('.removeTags').animate({opacity:1, height:"18px", padding:"10px 10px 10px 10px", borderWidth:"1px"},50);
        },
        stop: function() {
          $('.removeTags').animate({opacity:0, height:"0px", padding:"0px 0px 0px 10px", borderWidth:0},50);
        }
      });
    },

    removeTag: function(ev) {
      var tag = $(ev.currentTarget).html();
      var tags = this.model.get("tags").split(",");
      var position = tags.indexOf(tag);
      if(position > -1) {
        tags.splice(position, 1)
      }
      this.model.save({tags: tags.join(',')});
      this.model.trigger('tagRemoved');
      // this.trigger('tagRemoved');
      this.model.trigger('updated');

    },

    clean: function() {
      this.trigger('clean');
      this.elder('clean');
    },

    /**
     * Create the privacy selector after a "privacy link" clicked
     */
    _addPrivacySelector: function(ev) {
      ev && (ev.preventDefault());
      this.trigger('removePrivacy');

      this.privacy && this.privacy.clean();

      // this.options.limitation = true
      // Add privacy selector
      var privacy = this.privacy = new cdb.admin.PrivacySelector({
        model: this.model,
        limitation: this.options.limitation,
        upgrade_url: '/account/'+username+'/upgrade'
        // isn't any other way to catch the username than from a global?
      });

      cdb.god.bind("closeDialogs", this.privacy.hide, this.privacy);

      this.$el.append(this.privacy.render().el);

      this.privacy.show(ev.target);

      return false;
    },

    /**
     * Show delete confirmation after decides delete a table
     */
    _showDeleteConfirmation: function(ev) {
      var self = this;
      ev && (ev.preventDefault());
      this.trigger('deleteDialog');
      this.delete_dialog = new cdb.admin.DeleteDialog({
        model: this.model,
        config: this.options.config,
        ok: function() {
          self.deleted();
        }
      });
      this.delete_dialog.bind('deleting', function() { })
      this.delete_dialog.bind('deleted', function() { })
      this.retrigger('deleted', this.delete_dialog);

      $("body").append(this.delete_dialog.render().el);
      this.delete_dialog.open();
      this.delete_dialog.bind('deleting', self.deleting)
    },

    /**
     * Destroy droppable funcionality when el is being cleaned
     */
    _reClean: function() {
      this.clean();
      this.$el.droppable("destroy");
    }
  });

  /**
   * Tables list in the dashboard
   *
   * It will show up the user tables in a list
   *
   * Usage example:
   *
      var tableList = new cdb.admin.dashboard.TableList({
        el: this.$('#tablelist'),
        model: this.tables*,
        user: this.user**   // it needs it to know if the user has limitations or not
      });

      *   It needs a tables model to run correctly.
      **  It needs a user model to work properly.
   */

  var TableList = cdb.core.View.extend({
    tagName: 'ul',
    _TABLES_PER_PAGE: 10,
    events: {
      "click li:not('.disabled') a.delete": "markForDeletion"
    },

    initialize: function() {
      window.tl =  this;
      _.bindAll(this, "render", "appendTableByNumber", "_showLoader", "_hideLoader", "hardRender", "_updateListHeader", "_addAll", "_addTable", "_removeAllPrivacy", "markForDeletion");
      _.defaults(this.options, this.default_options);

      this.bindEvents();
    },

    bindEvents: function() {
      this.model.bind('reset',    this._addAll, this);
      this.model.bind('forceReload', this.hardRender, this);
      this.model.bind('reset',  this._updateListHeader, this);
      this.model.bind('updated',  this._updateListHeader, this);
      this.model.bind('add',  this._updateListHeader, this);

      this.model.bind('updating', this._showLoader, this)
      this.model.bind('reset',  this._hideLoader, this);
      this.model.bind('updated',  this._hideLoader, this);
      this.model.bind('add',  this._hideLoader, this);

      this.model.bind('elementAdded', this.appendTableByNumber, this);
    },

    hardRender: function() {
      this.lastMarkedForDeletion = undefined;
      return this.render();
    },

    render: function() {
      var self = this;
      this.$el.html('');
      this._updateListHeader();
      if (this.model.length > 0) {
        this.model.each(function(m, i) {
          // if we are on the same position that the deleted table was, we insert the notification
          if(self.lastMarkedForDeletion) {
            if(self.lastMarkedForDeletion.pos == i) {
              self.showDeletedNotification();
            }
          }
          self._addTable(m);
          // each time a tag is removed, we forward the event to be able to refresh the tag list
          m.bind('tagRemoved', function() {
             self.model.trigger('tagRemoved');
             self.model.trigger('reset');
          });
          m.bind('tagAdded', function() {
             self.model.trigger('tagAdded');
             self.model.trigger('reset');
          });
          m.bind('deleted', function() {
            self.model.trigger('updated');
            self.model.trigger('reset');
          })

        });
        // if the lastMarkedForDeletion element was the last from the list, we shoudl add the notification at the end
        if(self.lastMarkedForDeletion && this.model.length <= self.lastMarkedForDeletion.pos) {
          this.showDeletedNotification();
        }
      } else {
        this._addEmpty();
      }
      this.trigger('renderComplete');
    },

    showDeletedNotification: function() {
      var self = this;
      if(this.lastMarkedForDeletion) {
        this.$('.notificationContainer').remove();

        var notificationTpl =
          '
Your table (' + this.lastMarkedForDeletion.name + ') has been deleted
' +
          'x';

        var $container = $('

');
        this.$el.append($container);
        if(this.notification && this.notification.destroy) {
          this.notification.removeData().unbind().remove().clean();
          delete this.notification;
        }
        this.notification = new cdb.ui.common.Notification({
          el: $container,
          timeout:10000,
          template: notificationTpl,
          hideMethod: 'fadeOut',
        });
        this.notification.unbind('notificationDeleted');
        this.notification.bind('notificationDeleted', function() {
          self.notificationShowing = false;
        })
        if(self.notificationShowing) {
          self.notification.open();
          self.notificationShowing = true;
        } else {
          self.notification.open('', function() {
            self.notificationShowing = true;
          });
        }

      }
    },

    _addEmpty: function() {
      this.$el.append(cdb.templates.getTemplate('dashboard/views/table_list_empty'))
    },

    /**
     * Add all list
     */
    _addAll: function() {

      if(!this.modelLoaded) {
        this.modelLoaded = true;
        this.render();
      } else {
        if(this.model.length == 0) {
          this.render();
          this._hideLoader();
        }
      }
    },

    /**
     * Add single table view
     */
    _addTable: function(m) {
      var self = this;
      var li = new TableView({ model: m, config: this.options.config, limitation: !this.options.user.get("private_tables") });
      li.bind('removePrivacy', this._removeAllPrivacy);
      this.$el.append(li.render().el);
      this.addView(li);
      this._updateListHeader();
      li.bind('saving', function() {
        self.model.trigger('updating');
      });
      li.bind('saved', function() {
        self.model.trigger('updated')
      })
      li.bind('deleted', function() {
        self.model.trigger('updated')
        self.model.trigger('reset');
        self.refreshTable();
      })
      li.bind('deleting', function(model) {
        self.model.trigger('updating');
        self.model.remove(model);
      })
    },

    /**
     * Checks if the table is uncompensated (has less tan _TABLES_PER_PAGE entries but has a next page)
     * and if needed, fills the gap
     */
    refreshTable: function() {
      var self = this;
      if(!this.checkTableListFull()) {
        this.model.refillTableList(this._TABLES_PER_PAGE);
      }
    },

    /**
     * Extract the n table from model and append it to the view
     * @param  {Integer} n
     */
    appendTableByNumber: function(n) {
      if(this.model.models[n]) {
        this._addTable(this.model.models[n]);
      }
    },

    /**
     * Checks if there are more less than ten tables loaded (because a deletion)
     * and that there are more tables after the current pages.
     * @return {[type]} [description]
     */
    checkTableListFull: function() {
      if(this.model.models.length < this._TABLES_PER_PAGE &&
        this.model.total_entries >= this._TABLES_PER_PAGE) {
          return false;
      }
      return true;
    },

    _removeAllPrivacy: function() {
      for(var v in this._subviews) {
        this._subviews[v].privacy && this._subviews[v].privacy.clean();
      }
    },

    markForDeletion: function(ev) {
      ev.preventDefault();
      ev.stopPropagation();

      var tableInfo = $(ev.currentTarget).parents('.tableRow');

      var tableIndex = this.$('.tableRow').index(tableInfo)
      this.lastMarkedForDeletion = {
        "pos": tableIndex,
        "name": this.$('.tableRow h3 a').html(),
        "li": tableInfo
      };

      self.notificationShowing = false;
      if(this.notification) {
        this.notification.hide()
      }
    },

    /**
     * After a table removed
     */
    _tableRemoved: function() {
      this._updateListHeader();
    },

    /**
     * Update the counter of tables
     */
    _updateListHeader: function(sync) {
      var title =  this.model.total_entries + " " + ( this.model.total_entries != 1 ? "tables" : "table" );

      if (this.model.options.attributes.tag_name != "")
        title += " with tag " + decodeURIComponent(this.model.options.attributes.tag_name) + "";

      if (this.model.options.attributes.q != "")
        title += " with " + this.model.options.attributes.q +  " found";

      if (this.model.options.attributes.q == "" && this.model.options.attributes.tag_name == "")
        title += " in your account";

      $("section.tables > div.head > h2").html(title);
    },

    /**
     * Show the loader when the table model is operating
     */
    _showLoader: function() {
      $("section.tables > div.head > div.loader").fadeIn();
    },

    /**
     * Hide the loader when the table model is operating
     */
    _hideLoader: function() {
      $("section.tables > div.head > div.loader").fadeOut();
    }
  });

  cdb.admin.dashboard.TableList = TableList;
})();
<|endoftext|>


<commit_before>var Promise = require('bluebird');
var fs = Promise.promisifyAll(require('fs'));
var path = require('path');
var _ = require('lodash');
var async = require('../async');
var await = require('../await');

var largest = async (function (dir) {

    // Enumerate all files and subfolders in 'dir' to get their stats.
    var files = await (fs.readdirAsync(dir));
    var paths = _.map(files, function (file) { return path.join(dir, file); });
    var stats = await (_.map(paths, function (path) { return fs.statAsync(path); }));

    // Find the largest file, recursing into subfolders.
    var result = { path: undefined, size: 0, searched: 0 };
    await (_.map(stats, async (function (stat, i) { //NB: new async func here so we can do them in parallel but get blocking semantics inside the func
        if (stat.isDirectory()) {
            var cand = await (largest(paths[i])); // recurse
        } else {
            var cand = { path: paths[i], size: stat.size, searched: 1 };
        }
        if (cand.size > result.size) {
            result.path = cand.path;
            result.size = cand.size;
        }
        result.searched += cand.searched;
    })));
    return result;
});

function nodeified(dir, callback) { largest(dir).nodeify(callback); }
module.exports = nodeified;

//TODO: remove...
nodeified(__dirname, function (err, result) {
    console.log(err || result);
});
<commit_msg>finished new algo in largest-asyncawait<commit_after>var Promise = require('bluebird');
var fs = Promise.promisifyAll(require('fs'));
var path = require('path');
var Buffer = require('buffer').Buffer;
var _ = require('lodash');
var async = require('../async');
var await = require('../await');

var largest = async (function (dir) {

    // Enumerate all files and subfolders in 'dir' to get their stats.
    var files = await (fs.readdirAsync(dir));
    var paths = _.map(files, function (file) { return path.join(dir, file); });
    var stats = await (_.map(paths, function (path) { return fs.statAsync(path); }));
    var sizes = _.map(stats, function (stat) { return stat.size; });

    // Find the largest file, recursing into subfolders.
    var best = undefined; // index into paths/sizes arrays of largest file considered so far.
    var searched = 0; // number of files/folders considered so far.
    var previews = []; // preview of the contents of the largest files considered so far.
    await (_.map(stats, async (function (stat, i) { // NB: use an async func here so _.map runs in parallel,
                                                    // but we get blocking semantics inside the func
        if (stat.isDirectory()) {
            var subdirLargest = await (largest(paths[i])); // recurse into subfolder and await result.
            if (subdirLargest) { // will return nothing if subdir was empty.
                paths[i] = subdirLargest.path;
                sizes[i] = subdirLargest.size;
                previews[i] = subdirLargest.preview;
            }
        }
        searched += (subdirLargest ? subdirLargest.searched : 1);
        if (best === undefined || sizes[i] > sizes[best]) best = i;
    })));

    // If there were no files, return undefined now.
    if (best === undefined) return undefined;

    // Get a preview of the file contents, if we don't already have one.
    if (previews[best] === undefined) {
        var fd = await (fs.openAsync(paths[best], 'r'));
        var buffer = new Buffer(40);
        var bytesRead = await (fs.readAsync(fd, buffer, 0, 40, 0));
        previews[best] = buffer.toString('utf-8', 0, bytesRead);
    }

    // Return the path, size, preview, and searched in a single result.
    return { path: paths[best], size: sizes[best], preview: previews[best], searched: searched };
});

function nodeified(dir, callback) { largest(dir).nodeify(callback); }
module.exports = nodeified;

//TODO: remove...
nodeified(path.join(__dirname, '../..'), function (err, result) {
    console.log(err || result);
});

//TODO: not working for ../..
<|endoftext|>