You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@karaf.apache.org by jb...@apache.org on 2016/02/14 11:34:33 UTC
[18/19] karaf-decanter git commit: KARAF-4210 - Add
elasticsearch-head 1.x and 2.x features
http://git-wip-us.apache.org/repos/asf/karaf-decanter/blob/da345201/elasticsearch-head-1.x/src/main/resources/app.js
----------------------------------------------------------------------
diff --git a/elasticsearch-head-1.x/src/main/resources/app.js b/elasticsearch-head-1.x/src/main/resources/app.js
new file mode 100644
index 0000000..008f0b0
--- /dev/null
+++ b/elasticsearch-head-1.x/src/main/resources/app.js
@@ -0,0 +1,4373 @@
+(function() {
+
+ var window = this,
+ $ = jQuery;
+
+ function ns( namespace ) {
+ return (namespace || "").split(".").reduce( function( space, name ) {
+ return space[ name ] || ( space[ name ] = { ns: ns } );
+ }, this );
+ }
+
+ var app = ns("app");
+
+ var acx = ns("acx");
+
+ /**
+ * object iterator, returns an array with one element for each property of the object
+ * @function
+ */
+ acx.eachMap = function(obj, fn, thisp) {
+ var ret = [];
+ for(var n in obj) {
+ ret.push(fn.call(thisp, n, obj[n], obj));
+ }
+ return ret;
+ };
+
+ /**
+ * augments the first argument with the properties of the second and subsequent arguments
+ * like {@link $.extend} except that existing properties are not overwritten
+ */
+ acx.augment = function() {
+ var args = Array.prototype.slice.call(arguments),
+ src = (args.length === 1) ? this : args.shift(),
+ augf = function(n, v) {
+ if(! (n in src)) {
+ src[n] = v;
+ }
+ };
+ for(var i = 0; i < args.length; i++) {
+ $.each(args[i], augf);
+ }
+ return src;
+ };
+
+ /**
+ * tests whether the argument is an array
+ * @function
+ */
+ acx.isArray = $.isArray;
+
+ /**
+ * tests whether the argument is an object
+ * @function
+ */
+ acx.isObject = function (value) {
+ return Object.prototype.toString.call(value) == "[object Object]";
+ };
+
+ /**
+ * tests whether the argument is a function
+ * @function
+ */
+ acx.isFunction = $.isFunction;
+
+ /**
+ * tests whether the argument is a date
+ * @function
+ */
+ acx.isDate = function (value) {
+ return Object.prototype.toString.call(value) == "[object Date]";
+ };
+
+ /**
+ * tests whether the argument is a regexp
+ * @function
+ */
+ acx.isRegExp = function (value) {
+ return Object.prototype.toString.call(value) == "[object RegExp]";
+ };
+
+ /**
+ * tests whether the value is blank or empty
+ * @function
+ */
+ acx.isEmpty = function (value, allowBlank) {
+ return value === null || value === undefined || ((acx.isArray(value) && !value.length)) || (!allowBlank ? value === '' : false);
+ };
+
+ /**
+ * data type for performing chainable geometry calculations<br>
+ * can be initialised x,y | {x, y} | {left, top}
+ */
+ acx.vector = function(x, y) {
+ return new acx.vector.prototype.Init(x, y);
+ };
+
+ acx.vector.prototype = {
+ Init : function(x, y) {
+ x = x || 0;
+ this.y = isFinite(x.y) ? x.y : (isFinite(x.top) ? x.top : (isFinite(y) ? y : 0));
+ this.x = isFinite(x.x) ? x.x : (isFinite(x.left) ? x.left : (isFinite(x) ? x : 0));
+ },
+
+ add : function(i, j) {
+ var d = acx.vector(i, j);
+ return new this.Init(this.x + d.x, this.y + d.y);
+ },
+
+ sub : function(i, j) {
+ var d = acx.vector(i, j);
+ return new this.Init(this.x - d.x, this.y - d.y);
+ },
+
+ addX : function(i) {
+ return new this.Init(this.x + i, this.y);
+ },
+
+ addY : function(j) {
+ return new this.Init(this.x, this.y + j);
+ },
+
+ mod : function(fn) { // runs a function against the x and y values
+ return new this.Init({x: fn.call(this, this.x, "x"), y: fn.call(this, this.y, "y")});
+ },
+
+ /** returns true if this is within a rectangle formed by the points p and q */
+ within : function(p, q) {
+ return ( this.x >= ((p.x < q.x) ? p.x : q.x) && this.x <= ((p.x > q.x) ? p.x : q.x) &&
+ this.y >= ((p.y < q.y) ? p.y : q.y) && this.y <= ((p.y > q.y) ? p.y : q.y) );
+ },
+
+ asOffset : function() {
+ return { top: this.y, left: this.x };
+ },
+
+ asSize : function() {
+ return { height: this.y, width: this.x };
+ }
+ };
+
+ acx.vector.prototype.Init.prototype = acx.vector.prototype;
+
+ /**
+ * short cut functions for working with vectors and jquery.
+ * Each function returns the equivalent jquery value in a two dimentional vector
+ */
+ $.fn.vSize = function() { return acx.vector(this.width(), this.height()); };
+ $.fn.vOuterSize = function(margin) { return acx.vector(this.outerWidth(margin), this.outerHeight(margin)); };
+ $.fn.vScroll = function() { return acx.vector(this.scrollLeft(), this.scrollTop()); };
+ $.fn.vOffset = function() { return acx.vector(this.offset()); };
+ $.fn.vPosition = function() { return acx.vector(this.position()); };
+ $.Event.prototype.vMouse = function() { return acx.vector(this.pageX, this.pageY); };
+
+ /**
+ * object extensions (ecma5 compatible)
+ */
+ acx.augment(Object, {
+ keys: function(obj) {
+ var ret = [];
+ for(var n in obj) if(Object.prototype.hasOwnProperty.call(obj, n)) ret.push(n);
+ return ret;
+ }
+ });
+
+ /**
+ * Array prototype extensions
+ */
+ acx.augment(Array.prototype, {
+ 'contains' : function(needle) {
+ return this.indexOf(needle) !== -1;
+ },
+
+ // returns a new array consisting of all the members that are in both arrays
+ 'intersection' : function(b) {
+ var ret = [];
+ for(var i = 0; i < this.length; i++) {
+ if(b.contains(this[i])) {
+ ret.push(this[i]);
+ }
+ }
+ return ret;
+ },
+
+ 'remove' : function(value) {
+ var i = this.indexOf(value);
+ if(i !== -1) {
+ this.splice(i, 1);
+ }
+ }
+ });
+
+ /**
+ * String prototype extensions
+ */
+ acx.augment(String.prototype, {
+ 'contains' : function(needle) {
+ return this.indexOf(needle) !== -1;
+ },
+
+ 'equalsIgnoreCase' : function(match) {
+ return this.toLowerCase() === match.toLowerCase();
+ },
+
+ 'escapeHtml' : function() {
+ return this.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
+ },
+
+ 'escapeJS' : function() {
+ var meta = {'"':'\\"', '\\':'\\\\', '/':'\\/', '\b':'\\b', '\f':'\\f', '\n':'\\n', '\r':'\\r', '\t':'\\t'},
+ xfrm = function(c) { return meta[c] || "\\u" + c.charCodeAt(0).toString(16).zeroPad(4); };
+ return this.replace(new RegExp('(["\\\\\x00-\x1f\x7f-\uffff])', 'g'), xfrm);
+ },
+
+ 'escapeRegExp' : function() {
+ var ret = "", esc = "\\^$*+?.()=|{,}[]-";
+ for ( var i = 0; i < this.length; i++) {
+ ret += (esc.contains(this.charAt(i)) ? "\\" : "") + this.charAt(i);
+ }
+ return ret;
+ },
+
+ 'zeroPad' : function(len) {
+ return ("0000000000" + this).substring(this.length - len + 10);
+ }
+ });
+
+ $.fn.forEach = Array.prototype.forEach;
+
+ // joey / jquery integration
+ $.joey = function( obj ) {
+ return $( window.joey( obj ) );
+ };
+
+ window.joey.plugins.push( function( obj ) {
+ if( obj instanceof jQuery ) {
+ return obj[0];
+ }
+ });
+
+})();
+
+/**
+ * base class for creating inheritable classes
+ * based on resigs 'Simple Javascript Inheritance Class' (based on base2 and prototypejs)
+ * modified with static super and auto config
+ * @name Class
+ * @constructor
+ */
+(function( $, app ){
+
+ var ux = app.ns("ux");
+
+ var initializing = false, fnTest = /\b_super\b/;
+
+ ux.Class = function(){};
+
+ ux.Class.extend = function(prop) {
+ function Class() {
+ if(!initializing) {
+ var args = Array.prototype.slice.call(arguments);
+ this.config = $.extend( function(t) { // automatically construct a config object based on defaults and last item passed into the constructor
+ return $.extend(t._proto && t._proto() && arguments.callee(t._proto()) || {}, t.defaults);
+ } (this) , args.pop() );
+ this.init && this.init.apply(this, args); // automatically run the init function when class created
+ }
+ }
+
+ initializing = true;
+ var prototype = new this();
+ initializing = false;
+
+ var _super = this.prototype;
+ prototype._proto = function() {
+ return _super;
+ };
+
+ for(var name in prop) {
+ prototype[name] = typeof prop[name] === "function" && typeof _super[name] === "function" && fnTest.test(prop[name]) ?
+ (function(name, fn){
+ return function() { this._super = _super[name]; return fn.apply(this, arguments); };
+ })(name, prop[name]) : prop[name];
+ }
+
+ Class.prototype = prototype;
+ Class.constructor = Class;
+
+ Class.extend = arguments.callee; // make class extendable
+
+ return Class;
+ };
+})( this.jQuery, this.app );
+
+(function( app ) {
+
+ var ut = app.ns("ut");
+
+ ut.option_template = function(v) { return { tag: "OPTION", value: v, text: v }; };
+
+ ut.require_template = function(f) { return f.require ? { tag: "SPAN", cls: "require", text: "*" } : null; };
+
+
+ var sib_prefix = ['B','ki','Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi', 'Yi'];
+
+ ut.byteSize_template = function(n) {
+ var i = 0;
+ while( n >= 1000 ) {
+ i++;
+ n /= 1024;
+ }
+ return (i === 0 ? n.toString() : n.toFixed( 3 - parseInt(n,10).toString().length )) + ( sib_prefix[ i ] || "..E" );
+ };
+
+ var sid_prefix = ['','k','M', 'G', 'T', 'P', 'E', 'Z', 'Y'];
+
+ ut.count_template = function(n) {
+ var i = 0;
+ while( n >= 1000 ) {
+ i++;
+ n /= 1000;
+ }
+ return i === 0 ? n.toString() : ( n.toFixed( 3 - parseInt(n,10).toString().length ) + ( sid_prefix[ i ] || "..E" ) );
+ };
+
+})( this.app );
+
+(function( app ) {
+
+ var ux = app.ns("ux");
+
+ ux.Observable = ux.Class.extend((function() {
+ return {
+ init: function() {
+ this.observers = {};
+ for( var opt in this.config ) { // automatically install observers that are defined in the configuration
+ if( opt.indexOf( 'on' ) === 0 ) {
+ this.on( opt.substring(2) , this.config[ opt ] );
+ }
+ }
+ },
+ _getObs: function( type ) {
+ return ( this.observers[ type.toLowerCase() ] || ( this.observers[ type.toLowerCase() ] = [] ) );
+ },
+ on: function( type, fn, params, thisp ) {
+ this._getObs( type ).push( { "cb" : fn, "args" : params || [] , "cx" : thisp || this } );
+ return this;
+ },
+ fire: function( type ) {
+ var params = Array.prototype.slice.call( arguments, 1 );
+ this._getObs( type ).slice().forEach( function( ob ) {
+ ob["cb"].apply( ob["cx"], ob["args"].concat( params ) );
+ } );
+ return this;
+ },
+ removeAllObservers: function() {
+ this.observers = {};
+ },
+ removeObserver: function( type, fn ) {
+ var obs = this._getObs( type ),
+ index = obs.reduce( function(p, t, i) { return (t.cb === fn) ? i : p; }, -1 );
+ if(index !== -1) {
+ obs.splice( index, 1 );
+ }
+ return this; // make observable functions chainable
+ },
+ hasObserver: function( type ) {
+ return !! this._getObs( type ).length;
+ }
+ };
+ })());
+
+})( this.app );
+(function( app ) {
+
+ var ux = app.ns("ux");
+
+ var extend = ux.Observable.extend;
+ var instance = function() {
+ if( ! ("me" in this) ) {
+ this.me = new this();
+ }
+ return this.me;
+ };
+
+ ux.Singleton = ux.Observable.extend({});
+
+ ux.Singleton.extend = function() {
+ var Self = extend.apply( this, arguments );
+ Self.instance = instance;
+ return Self;
+ };
+
+})( this.app );
+
+(function( $, app ) {
+
+ var ux = app.ns("ux");
+
+ /**
+ * Provides drag and drop functionality<br>
+ * a DragDrop instance is created for each usage pattern and then used over and over again<br>
+ * first a dragObj is defined - this is the jquery node that will be dragged around<br>
+ * second, the event callbacks are defined - these allow you control the ui during dragging and run functions when successfully dropping<br>
+ * thirdly drop targets are defined - this is a list of DOM nodes, the constructor works in one of two modes:
+ * <li>without targets - objects can be picked up and dragged around, dragStart and dragStop events fire</li>
+ * <li>with targets - as objects are dragged over targets dragOver, dragOut and DragDrop events fire
+ * to start dragging call the DragDrop.pickup_handler() function, dragging stops when the mouse is released.
+ * @constructor
+ * The following options are supported
+ * <dt>targetSelector</dt>
+ * <dd>an argument passed directly to jquery to create a list of targets, as such it can be a CSS style selector, or an array of DOM nodes<br>if target selector is null the DragDrop does Drag only and will not fire dragOver dragOut and dragDrop events</dd>
+ * <dt>pickupSelector</dt>
+ * <dd>a jquery selector. The pickup_handler is automatically bound to matched elements (eg clicking on these elements starts the drag). if pickupSelector is null, the pickup_handler must be manually bound <code>$(el).bind("mousedown", dragdrop.pickup_handler)</code></dd>
+ * <dt>dragObj</dt>
+ * <dd>the jQuery element to drag around when pickup is called. If not defined, dragObj must be set in onDragStart</dd>
+ * <dt>draggingClass</dt>
+ * <dd>the class(es) added to items when they are being dragged</dd>
+ * The following observables are supported
+ * <dt>dragStart</dt>
+ * <dd>a callback when start to drag<br><code>function(jEv)</code></dd>
+ * <dt>dragOver</dt>
+ * <dd>a callback when we drag into a target<br><code>function(jEl)</code></dd>
+ * <dt>dragOut</dt>
+ * <dd>a callback when we drag out of a target, or when we drop over a target<br><code>function(jEl)</code></dd>
+ * <dt>dragDrop</dt>
+ * <dd>a callback when we drop on a target<br><code>function(jEl)</code></dd>
+ * <dt>dragStop</dt>
+ * <dd>a callback when we stop dragging<br><code>function(jEv)</code></dd>
+ */
+ ux.DragDrop = ux.Observable.extend({
+ defaults : {
+ targetsSelector : null,
+ pickupSelector: null,
+ dragObj : null,
+ draggingClass : "dragging"
+ },
+
+ init: function(options) {
+ this._super(); // call the class initialiser
+
+ this.drag_handler = this.drag.bind(this);
+ this.drop_handler = this.drop.bind(this);
+ this.pickup_handler = this.pickup.bind(this);
+ this.targets = [];
+ this.dragObj = null;
+ this.dragObjOffset = null;
+ this.currentTarget = null;
+ if(this.config.pickupSelector) {
+ $(this.config.pickupSelector).bind("mousedown", this.pickup_handler);
+ }
+ },
+
+ drag : function(jEv) {
+ jEv.preventDefault();
+ var mloc = acx.vector( this.lockX || jEv.pageX, this.lockY || jEv.pageY );
+ this.dragObj.css(mloc.add(this.dragObjOffset).asOffset());
+ if(this.targets.length === 0) {
+ return;
+ }
+ if(this.currentTarget !== null && mloc.within(this.currentTarget[1], this.currentTarget[2])) {
+ return;
+ }
+ if(this.currentTarget !== null) {
+ this.fire('dragOut', this.currentTarget[0]);
+ this.currentTarget = null;
+ }
+ for(var i = 0; i < this.targets.length; i++) {
+ if(mloc.within(this.targets[i][1], this.targets[i][2])) {
+ this.currentTarget = this.targets[i];
+ break;
+ }
+ }
+ if(this.currentTarget !== null) {
+ this.fire('dragOver', this.currentTarget[0]);
+ }
+ },
+
+ drop : function(jEv) {
+ $(document).unbind("mousemove", this.drag_handler);
+ $(document).unbind("mouseup", this.drop_handler);
+ this.dragObj.removeClass(this.config.draggingClass);
+ if(this.currentTarget !== null) {
+ this.fire('dragOut', this.currentTarget[0]);
+ this.fire('dragDrop', this.currentTarget[0]);
+ }
+ this.fire('dragStop', jEv);
+ this.dragObj = null;
+ },
+
+ pickup : function(jEv, opts) {
+ $.extend(this.config, opts);
+ this.fire('dragStart', jEv);
+ this.dragObj = this.dragObj || this.config.dragObj;
+ this.dragObjOffset = this.config.dragObjOffset || acx.vector(this.dragObj.offset()).sub(jEv.pageX, jEv.pageY);
+ this.lockX = this.config.lockX ? jEv.pageX : 0;
+ this.lockY = this.config.lockY ? jEv.pageY : 0;
+ this.dragObj.addClass(this.config.draggingClass);
+ if(!this.dragObj.get(0).parentNode || this.dragObj.get(0).parentNode.nodeType === 11) { // 11 = document fragment
+ $(document.body).append(this.dragObj);
+ }
+ if(this.config.targetsSelector) {
+ this.currentTarget = null;
+ var targets = ( this.targets = [] );
+ // create an array of elements optimised for rapid collision detection calculation
+ $(this.config.targetsSelector).each(function(i, el) {
+ var jEl = $(el);
+ var tl = acx.vector(jEl.offset());
+ var br = tl.add(jEl.width(), jEl.height());
+ targets.push([jEl, tl, br]);
+ });
+ }
+ $(document).bind("mousemove", this.drag_handler);
+ $(document).bind("mouseup", this.drop_handler);
+ this.drag_handler(jEv);
+ }
+ });
+
+})( this.jQuery, this.app );
+
+(function( app ) {
+
+ var ux = app.ns("ux");
+
+ ux.FieldCollection = ux.Observable.extend({
+ defaults: {
+ fields: [] // the collection of fields
+ },
+ init: function() {
+ this._super();
+ this.fields = this.config.fields;
+ },
+ validate: function() {
+ return this.fields.reduce(function(r, field) {
+ return r && field.validate();
+ }, true);
+ },
+ getData: function(type) {
+ return this.fields.reduce(function(r, field) {
+ r[field.name] = field.val(); return r;
+ }, {});
+ }
+ });
+
+})( this.app );
+
+(function( $, app ) {
+
+ var data = app.ns("data");
+ var ux = app.ns("ux");
+
+ data.Model = ux.Observable.extend({
+ defaults: {
+ data: null
+ },
+ init: function() {
+ this.set( this.config.data );
+ },
+ set: function( key, value ) {
+ if( arguments.length === 1 ) {
+ this._data = $.extend( {}, key );
+ } else {
+ key.split(".").reduce(function( ptr, prop, i, props) {
+ if(i === (props.length - 1) ) {
+ ptr[prop] = value;
+ } else {
+ if( !(prop in ptr) ) {
+ ptr[ prop ] = {};
+ }
+ return ptr[prop];
+ }
+ }, this._data );
+ }
+ },
+ get: function( key ) {
+ return key.split(".").reduce( function( ptr, prop ) {
+ return ( ptr && ( prop in ptr ) ) ? ptr[ prop ] : undefined;
+ }, this._data );
+ },
+ });
+})( this.jQuery, this.app );
+
+(function( app ) {
+
+ var data = app.ns("data");
+ var ux = app.ns("ux");
+
+ data.DataSourceInterface = ux.Observable.extend({
+ /*
+ properties
+ meta = { total: 0 },
+ headers = [ { name: "" } ],
+ data = [ { column: value, column: value } ],
+ sort = { column: "name", dir: "desc" }
+ events
+ data: function( DataSourceInterface )
+ */
+ _getSummary: function(res) {
+ this.summary = i18n.text("TableResults.Summary", res._shards.successful, res._shards.total, res.hits.total, (res.took / 1000).toFixed(3));
+ },
+ _getMeta: function(res) {
+ this.meta = { total: res.hits.total, shards: res._shards, tool: res.took };
+ }
+ });
+
+})( this.app );
+(function( app ) {
+
+ var data = app.ns("data");
+
+ data.ResultDataSourceInterface = data.DataSourceInterface.extend({
+ results: function(res) {
+ this._getSummary(res);
+ this._getMeta(res);
+ this._getData(res);
+ this.sort = {};
+ this.fire("data", this);
+ },
+ _getData: function(res) {
+ var columns = this.columns = [];
+ this.data = res.hits.hits.map(function(hit) {
+ var row = (function(path, spec, row) {
+ for(var prop in spec) {
+ if(acx.isObject(spec[prop])) {
+ arguments.callee(path.concat(prop), spec[prop], row);
+ } else if(acx.isArray(spec[prop])) {
+ if(spec[prop].length) {
+ arguments.callee(path.concat(prop), spec[prop][0], row)
+ }
+ } else {
+ var dpath = path.concat(prop).join(".");
+ if(! columns.contains(dpath)) {
+ columns.push(dpath);
+ }
+ row[dpath] = (spec[prop] || "null").toString();
+ }
+ }
+ return row;
+ })([ hit._type ], hit, {});
+ row._source = hit;
+ return row;
+ }, this);
+ }
+ });
+
+})( this.app );
+
+(function( app ) {
+
+ /*
+ notes on elasticsearch terminology used in this project
+
+ indices[index] contains one or more
+ types[type] contains one or more
+ documents contain one or more
+ paths[path]
+ each path contains one element of data
+ each path maps to one field
+
+ eg PUT, "/twitter/tweet/1"
+ {
+ user: "mobz",
+ date: "2011-01-01",
+ message: "You know, for browsing elasticsearch",
+ name: {
+ first: "Ben",
+ last: "Birch"
+ }
+ }
+
+ creates
+ 1 index: twitter
+ this is the collection of index data
+ 1 type: tweet
+ this is the type of document (kind of like a table in sql)
+ 1 document: /twitter/tweet/1
+ this is an actual document in the index ( kind of like a row in sql)
+ 5 paths: [ ["user"], ["date"], ["message"], ["name","first"], ["name","last"] ]
+ since documents can be heirarchical this maps a path from a document root to a piece of data
+ 5 fields: [ "user", "date", "message", "first", "last" ]
+ this is an indexed 'column' of data. fields are not heirarchical
+
+ the relationship between a path and a field is called a mapping. mappings also contain a wealth of information about how es indexes the field
+
+ notes
+ 1) a path is stored as an array, the dpath is <index> . <type> . path.join("."),
+ which can be considered the canonical reference for a mapping
+ 2) confusingly, es uses the term index for both the collection of indexed data, and the individually indexed fields
+ so the term index_name is the same as field_name in this sense.
+
+ */
+
+ var data = app.ns("data");
+ var ux = app.ns("ux");
+
+ var coretype_map = {
+ "string" : "string",
+ "long" : "number",
+ "integer" : "number",
+ "float" : "number",
+ "double" : "number",
+ "ip" : "number",
+ "date" : "date",
+ "boolean" : "boolean",
+ "binary" : "binary",
+ "multi_field" : "multi_field"
+ };
+
+ var default_property_map = {
+ "string" : { "store" : "no", "index" : "analysed" },
+ "number" : { "store" : "no", "precision_steps" : 4 },
+ "date" : { "store" : "no", "format" : "dateOptionalTime", "index": "yes", "precision_steps": 4 },
+ "boolean" : { "store" : "no", "index": "yes" },
+ "binary" : { },
+ "multi_field" : { }
+ };
+
+ // parses metatdata from a cluster, into a bunch of useful data structures
+ data.MetaData = ux.Observable.extend({
+ defaults: {
+ state: null // (required) response from a /_cluster/state request
+ },
+ init: function() {
+ this._super();
+ this.refresh(this.config.state);
+ },
+ getIndices: function(alias) {
+ return alias ? this.aliases[alias] : this.indicesList;
+ },
+ // returns an array of strings containing all types that are in all of the indices passed in, or all types
+ getTypes: function(indices) {
+ var indices = indices || [], types = [];
+ this.typesList.forEach(function(type) {
+ for(var i = 0; i < indices.length; i++) {
+ if(! this.indices[indices[i]].types.contains(type))
+ return;
+ }
+ types.push(type);
+ }, this);
+ return types;
+ },
+ refresh: function(state) {
+ // currently metadata expects all like named fields to have the same type, even when from different types and indices
+ var aliases = this.aliases = {};
+ var indices = this.indices = {};
+ var types = this.types = {};
+ var fields = this.fields = {};
+ var paths = this.paths = {};
+
+ function createField( mapping, index, type, path, name ) {
+ var dpath = [ index, type ].concat( path ).join( "." );
+ var field_name = mapping.index_name || name;
+ var field = paths[ dpath ] = fields[ field_name ] || $.extend({
+ field_name : field_name,
+ core_type : coretype_map[ mapping.type ],
+ dpaths : []
+ }, default_property_map[ coretype_map[ mapping.type ] ], mapping );
+
+ if (field.type === "multi_field" && typeof field.fields !== "undefined") {
+ for (var subField in field.fields) {
+ field.fields[ subField ] = createField( field.fields[ subField ], index, type, path.concat( subField ), name + "." + subField );
+ }
+ }
+ if (fields.dpaths) {
+ field.dpaths.push(dpath);
+ }
+ return field;
+ }
+ function getFields(properties, type, index, listeners) {
+ (function procPath(prop, path) {
+ for (var n in prop) {
+ if ("properties" in prop[n]) {
+ procPath( prop[ n ].properties, path.concat( n ) );
+ } else {
+ var field = createField(prop[n], index, type, path.concat(n), n);
+ listeners.forEach( function( listener ) {
+ listener[ field.field_name ] = field;
+ } );
+ }
+ }
+ })(properties, []);
+ }
+ for (var index in state.metadata.indices) {
+ indices[index] = {
+ types : [], fields : {}, paths : {}, parents : {}
+ };
+ indices[index].aliases = state.metadata.indices[index].aliases;
+ indices[index].aliases.forEach(function(alias) {
+ (aliases[alias] || (aliases[alias] = [])).push(index);
+ });
+ var mapping = state.metadata.indices[index].mappings;
+ for (var type in mapping) {
+ indices[index].types.push(type);
+ if ( type in types) {
+ types[type].indices.push(index);
+ } else {
+ types[type] = {
+ indices : [index], fields : {}
+ };
+ }
+ getFields(mapping[type].properties, type, index, [fields, types[type].fields, indices[index].fields]);
+ if ( typeof mapping[type]._parent !== "undefined") {
+ indices[index].parents[type] = mapping[type]._parent.type;
+ }
+ }
+ }
+
+ this.aliasesList = Object.keys(aliases);
+ this.indicesList = Object.keys(indices);
+ this.typesList = Object.keys(types);
+ this.fieldsList = Object.keys(fields);
+ }
+ });
+
+})( this.app );
+
+(function( app ) {
+
+ var data = app.ns("data");
+ var ux = app.ns("ux");
+
+ data.MetaDataFactory = ux.Observable.extend({
+ defaults: {
+ cluster: null // (required) an app.services.Cluster
+ },
+ init: function() {
+ this._super();
+ this.config.cluster.get("_cluster/state", function(data) {
+ this.metaData = new app.data.MetaData({state: data});
+ this.fire("ready", this.metaData, { originalData: data }); // TODO originalData needed for legacy ui.FilterBrowser
+ }.bind(this));
+ }
+ });
+
+})( this.app );
+
+(function( app ) {
+
+ var data = app.ns("data");
+ var ux = app.ns("ux");
+
+ data.Query = ux.Observable.extend({
+ defaults: {
+ cluster: null, // (required) instanceof app.services.Cluster
+ size: 50 // size of pages to return
+ },
+ init: function() {
+ this._super();
+ this.cluster = this.config.cluster;
+ this.refuid = 0;
+ this.refmap = {};
+ this.indices = [];
+ this.types = [];
+ this.search = {
+ fields : [ "_parent", "_source" ],
+ query: { bool: { must: [], must_not: [], should: [] } },
+ from: 0,
+ size: this.config.size,
+ sort: [],
+ facets: {},
+ version: true
+ };
+ this.defaultClause = this.addClause();
+ this.history = [ this.getState() ];
+ },
+ clone: function() {
+ var q = new data.Query({ cluster: this.cluster });
+ q.restoreState(this.getState());
+ for(var uqid in q.refmap) {
+ q.removeClause(uqid);
+ }
+ return q;
+ },
+ getState: function() {
+ return $.extend(true, {}, { search: this.search, indices: this.indices, types: this.types });
+ },
+ restoreState: function(state) {
+ state = $.extend(true, {}, state || this.history[this.history.length - 1]);
+ this.indices = state.indices;
+ this.types = state.types;
+ this.search = state.search;
+ },
+ getData: function() {
+ return JSON.stringify(this.search);
+ },
+ query: function() {
+ var state = this.getState();
+ this.cluster.post(
+ (this.indices.join(",") || "_all") + "/" + ( this.types.length ? this.types.join(",") + "/" : "") + "_search",
+ this.getData(),
+ function(results) {
+ if(results === null) {
+ alert(i18n.text("Query.FailAndUndo"));
+ this.restoreState();
+ return;
+ }
+ this.history.push(state);
+
+ this.fire("results", this, results);
+ }.bind(this));
+ },
+ loadParents: function(res,metadata){
+ //create data for mget
+ var data = { docs :[] };
+ var indexToTypeToParentIds = {};
+ res.hits.hits.forEach(function(hit) {
+ if (typeof hit.fields != "undefined"){
+ if (typeof hit.fields._parent != "undefined"){
+ var parentType = metadata.indices[hit._index].parents[hit._type];
+ if (typeof indexToTypeToParentIds[hit._index] == "undefined"){
+ indexToTypeToParentIds[hit._index] = new Object();
+ }
+ if (typeof indexToTypeToParentIds[hit._index][hit._type] == "undefined"){
+ indexToTypeToParentIds[hit._index][hit._type] = new Object();
+ }
+ if (typeof indexToTypeToParentIds[hit._index][hit._type][hit.fields._parent] == "undefined"){
+ indexToTypeToParentIds[hit._index][hit._type][hit.fields._parent] = null;
+ data.docs.push({ _index:hit._index, _type:parentType, _id:hit.fields._parent});
+ }
+ }
+ }
+ });
+
+ //load parents
+ var state = this.getState();
+ this.cluster.post("_mget",JSON.stringify(data),
+ function(results) {
+ if(results === null) {
+ alert(i18n.text("Query.FailAndUndo"));
+ this.restoreState();
+ return;
+ }
+ this.history.push(state);
+ var indexToTypeToParentIdToHit = new Object();
+ results.docs.forEach(function(doc) {
+ if (typeof indexToTypeToParentIdToHit[doc._index] == "undefined"){
+ indexToTypeToParentIdToHit[doc._index] = new Object();
+ }
+
+ if (typeof indexToTypeToParentIdToHit[doc._index][doc._type] == "undefined"){
+ indexToTypeToParentIdToHit[doc._index][doc._type] = new Object();
+ }
+
+ indexToTypeToParentIdToHit[doc._index][doc._type][doc._id] = doc;
+ });
+
+ res.hits.hits.forEach(function(hit) {
+ if (typeof hit.fields != "undefined"){
+ if (typeof hit.fields._parent != "undefined"){
+ var parentType = metadata.indices[hit._index].parents[hit._type];
+ hit._parent = indexToTypeToParentIdToHit[hit._index][parentType][hit.fields._parent];
+ }
+ }
+ });
+
+ this.fire("resultsWithParents", this, res);
+ }.bind(this));
+ },
+ setPage: function(page) {
+ this.search.from = this.config.size * (page - 1);
+ },
+ setSort: function(index, desc) {
+ var sortd = {}; sortd[index] = { reverse: !!desc };
+ this.search.sort.unshift( sortd );
+ for(var i = 1; i < this.search.sort.length; i++) {
+ if(Object.keys(this.search.sort[i])[0] === index) {
+ this.search.sort.splice(i, 1);
+ break;
+ }
+ }
+ },
+ setIndex: function(index, add) {
+ if(add) {
+ if(! this.indices.contains(index)) this.indices.push(index);
+ } else {
+ this.indices.remove(index);
+ }
+ this.fire("setIndex", this, { index: index, add: !!add });
+ },
+ setType: function(type, add) {
+ if(add) {
+ if(! this.types.contains(type)) this.types.push(type);
+ } else {
+ this.types.remove(type);
+ }
+ this.fire("setType", this, { type: type, add: !!add });
+ },
+ addClause: function(value, field, op, bool) {
+ bool = bool || "should";
+ op = op || "match_all";
+ field = field || "_all";
+ var clause = this._setClause(value, field, op, bool);
+ var uqid = "q-" + this.refuid++;
+ this.refmap[uqid] = { clause: clause, value: value, field: field, op: op, bool: bool };
+ if(this.search.query.bool.must.length + this.search.query.bool.should.length > 1) {
+ this.removeClause(this.defaultClause);
+ }
+ this.fire("queryChanged", this, { uqid: uqid, search: this.search} );
+ return uqid; // returns reference to inner query object to allow fast updating
+ },
+ removeClause: function(uqid) {
+ var ref = this.refmap[uqid],
+ bool = this.search.query.bool[ref.bool];
+ bool.remove(ref.clause);
+ if(this.search.query.bool.must.length + this.search.query.bool.should.length === 0) {
+ this.defaultClause = this.addClause();
+ }
+ },
+ addFacet: function(facet) {
+ var facetId = "f-" + this.refuid++;
+ this.search.facets[facetId] = facet;
+ this.refmap[facetId] = { facetId: facetId, facet: facet };
+ return facetId;
+ },
+ removeFacet: function(facetId) {
+ delete this.search.facets[facetId];
+ delete this.refmap[facetId];
+ },
+ _setClause: function(value, field, op, bool) {
+ var clause = {}, query = {};
+ if(op === "match_all") {
+ } else if(op === "query_string") {
+ query["default_field"] = field;
+ query["query"] = value;
+ } else if(op === "missing") {
+ op = "constant_score"
+ var missing = {}, filter = {};
+ missing["field"] = field;
+ filter["missing"] = missing
+ query["filter"] = filter;
+ } else {
+ query[field] = value;
+ }
+ clause[op] = query;
+ this.search.query.bool[bool].push(clause);
+ return clause;
+ }
+ });
+
+})( this.app );
+
+(function( app ) {
+
+ var data = app.ns("data");
+
+ data.QueryDataSourceInterface = data.DataSourceInterface.extend({
+ defaults: {
+ metadata: null, // (required) instanceof app.data.MetaData, the cluster metadata
+ query: null // (required) instanceof app.data.Query the data source
+ },
+ init: function() {
+ this._super();
+ this.config.query.on("results", this._results_handler.bind(this) );
+ this.config.query.on("resultsWithParents", this._load_parents.bind(this) );
+ },
+ _results_handler: function(query, res) {
+ this._getSummary(res);
+ this._getMeta(res);
+ var sort = query.search.sort[0] || { "_score": { reverse: false }};
+ var sortField = Object.keys(sort)[0];
+ this.sort = { column: sortField, dir: (sort[sortField].reverse ? "asc" : "desc") };
+ this._getData(res, this.config.metadata);
+ this.fire("data", this);
+ },
+ _load_parents: function(query, res) {
+ query.loadParents(res, this.config.metadata);
+ },
+ _getData: function(res, metadata) {
+ var metaColumns = ["_index", "_type", "_id", "_score"];
+ var columns = this.columns = [].concat(metaColumns);
+
+ this.data = res.hits.hits.map(function(hit) {
+ var row = (function(path, spec, row) {
+ for(var prop in spec) {
+ if(acx.isObject(spec[prop])) {
+ arguments.callee(path.concat(prop), spec[prop], row);
+ } else if(acx.isArray(spec[prop])) {
+ if(spec[prop].length) {
+ arguments.callee(path.concat(prop), spec[prop][0], row)
+ }
+ } else {
+ var dpath = path.concat(prop).join(".");
+ if(metadata.paths[dpath]) {
+ var field_name = metadata.paths[dpath].field_name;
+ if(! columns.contains(field_name)) {
+ columns.push(field_name);
+ }
+ row[field_name] = (spec[prop] === null ? "null" : spec[prop] ).toString();
+ } else {
+ // TODO: field not in metadata index
+ }
+ }
+ }
+ return row;
+ })([ hit._index, hit._type ], hit._source, {});
+ metaColumns.forEach(function(n) { row[n] = hit[n]; });
+ row._source = hit;
+ if (typeof hit._parent!= "undefined") {
+ (function(prefix, path, spec, row) {
+ for(var prop in spec) {
+ if(acx.isObject(spec[prop])) {
+ arguments.callee(prefix, path.concat(prop), spec[prop], row);
+ } else if(acx.isArray(spec[prop])) {
+ if(spec[prop].length) {
+ arguments.callee(prefix, path.concat(prop), spec[prop][0], row)
+ }
+ } else {
+ var dpath = path.concat(prop).join(".");
+ if(metadata.paths[dpath]) {
+ var field_name = metadata.paths[dpath].field_name;
+ var column_name = prefix+"."+field_name;
+ if(! columns.contains(column_name)) {
+ columns.push(column_name);
+ }
+ row[column_name] = (spec[prop] === null ? "null" : spec[prop] ).toString();
+ } else {
+ // TODO: field not in metadata index
+ }
+ }
+ }
+ })(hit._parent._type,[hit._parent._index, hit._parent._type], hit._parent._source, row);
+ }
+ return row;
+ }, this);
+ }
+ });
+
+})( this.app );
+
+(function( app ) {
+
+ var data = app.ns("data");
+ var ux = app.ns("ux");
+
+ data.BoolQuery = ux.Observable.extend({
+ defaults: {
+ size: 50 // size of pages to return
+ },
+ init: function() {
+ this._super();
+ this.refuid = 0;
+ this.refmap = {};
+ this.search = {
+ query: { bool: { must: [], must_not: [], should: [] } },
+ from: 0,
+ size: this.config.size,
+ sort: [],
+ facets: {}
+ };
+ this.defaultClause = this.addClause();
+ },
+ setSize: function(size) {
+ this.search.size = parseInt( size, 10 );
+ },
+ setPage: function(page) {
+ this.search.from = this.config.size * (page - 1) + 1;
+ },
+ addClause: function(value, field, op, bool) {
+ bool = bool || "should";
+ op = op || "match_all";
+ field = field || "_all";
+ var clause = this._setClause(value, field, op, bool);
+ var uqid = "q-" + this.refuid++;
+ this.refmap[uqid] = { clause: clause, value: value, field: field, op: op, bool: bool };
+ if(this.search.query.bool.must.length + this.search.query.bool.should.length > 1) {
+ this.removeClause(this.defaultClause);
+ }
+ this.fire("queryChanged", this, { uqid: uqid, search: this.search} );
+ return uqid; // returns reference to inner query object to allow fast updating
+ },
+ removeClause: function(uqid) {
+ var ref = this.refmap[uqid],
+ bool = this.search.query.bool[ref.bool];
+ var clauseIdx = bool.indexOf(ref.clause);
+ // Check that this clause hasn't already been removed
+ if (clauseIdx >=0) {
+ bool.splice(clauseIdx, 1);
+ }
+ },
+ _setClause: function(value, field, op, bool) {
+ var clause = {}, query = {};
+ if(op === "match_all") {
+ } else if(op === "query_string") {
+ query["default_field"] = field;
+ query["query"] = value;
+ } else if(op === "missing") {
+ op = "constant_score"
+ var missing = {}, filter = {};
+ missing["field"] = field;
+ filter["missing"] = missing
+ query["filter"] = filter;
+ } else {
+ query[field] = value;
+ }
+ clause[op] = query;
+ this.search.query.bool[bool].push(clause);
+ return clause;
+ },
+ getData: function() {
+ return JSON.stringify(this.search);
+ }
+ });
+
+})( this.app );
+(function( app ) {
+
+ var ux = app.ns("ux");
+ var services = app.ns("services");
+
+ services.Preferences = ux.Singleton.extend({
+ init: function() {
+ this._storage = window.localStorage;
+ this._setItem("__version", 1 );
+ },
+ get: function( key ) {
+ return this._getItem( key );
+ },
+ set: function( key, val ) {
+ return this._setItem( key, val );
+ },
+ _getItem: function( key ) {
+ try {
+ return JSON.parse( this._storage.getItem( key ) );
+ } catch(e) {
+ console.warn( e );
+ return undefined;
+ }
+ },
+ _setItem: function( key, val ) {
+ try {
+ return this._storage.setItem( key, JSON.stringify( val ) );
+ } catch(e) {
+ console.warn( e );
+ return undefined;
+ }
+ }
+ });
+
+})( this.app );
+
+(function( $, app ) {
+
+ var services = app.ns("services");
+ var ux = app.ns("ux");
+
+ function parse_version( v ) {
+ return v.match(/^(\d+)\.(\d+)\.(\d+)/).slice(1,4).map( function(d) { return parseInt(d || 0, 10); } );
+ }
+
+ services.Cluster = ux.Class.extend({
+ defaults: {
+ base_uri: null
+ },
+ init: function() {
+ this.base_uri = this.config.base_uri;
+ },
+ setVersion: function( v ) {
+ this.version = v;
+ this._version_parts = parse_version( v );
+ },
+ versionAtLeast: function( v ) {
+ var testVersion = parse_version( v );
+ for( var i = 0; i < 3; i++ ) {
+ if( testVersion[i] !== this._version_parts[i] ) {
+ return testVersion[i] < this._version_parts[i];
+ }
+ }
+ return true;
+ },
+ request: function( params ) {
+ return $.ajax( $.extend({
+ url: this.base_uri + params.path,
+ dataType: "json",
+ error: function(xhr, type, message) {
+ if("console" in window) {
+ console.log({ "XHR Error": type, "message": message });
+ }
+ }
+ }, params) );
+ },
+ "get": function(path, success) { return this.request( { type: "GET", path: path, success: success } ); },
+ "post": function(path, data, success) { return this.request( { type: "POST", path: path, data: data, success: success } ); },
+ "put": function(path, data, success) { return this.request( { type: "PUT", path: path, data: data, success: success } ); },
+ "delete": function(path, data, success) { return this.request( { type: "DELETE", path: path, data: data, success: success } ); }
+ });
+
+})( this.jQuery, this.app );
+
+(function( app ) {
+
+ var services = app.ns("services");
+ var ux = app.ns("ux");
+
+ services.ClusterState = ux.Observable.extend({
+ defaults: {
+ cluster: null
+ },
+ init: function() {
+ this._super();
+ this.cluster = this.config.cluster;
+ this.clusterState = null;
+ this.status = null;
+ this.nodeStats = null;
+ this.clusterNodes = null;
+ },
+ refresh: function() {
+ var self = this, clusterState, status, nodeStats, clusterNodes, clusterHealth;
+ function updateModel() {
+ if( clusterState && status && nodeStats && clusterNodes && clusterHealth ) {
+ this.clusterState = clusterState;
+ this.status = status;
+ this.nodeStats = nodeStats;
+ this.clusterNodes = clusterNodes;
+ this.clusterHealth = clusterHealth;
+ this.fire( "data", this );
+ }
+ }
+ this.cluster.get("_cluster/state", function( data ) {
+ clusterState = data;
+ updateModel.call( self );
+ });
+ this.cluster.get("_status", function( data ) {
+ status = data;
+ updateModel.call( self );
+ });
+ this.cluster.get("_nodes/stats?all=true", function( data ) {
+ nodeStats = data;
+ updateModel.call( self );
+ });
+ this.cluster.get("_nodes", function( data ) {
+ clusterNodes = data;
+ updateModel.call( self );
+ });
+ this.cluster.get("_cluster/health", function( data ) {
+ clusterHealth = data;
+ updateModel.call( self );
+ });
+ },
+ _clusterState_handler: function(state) {
+ this.clusterState = state;
+ this.redraw("clusterState");
+ },
+ _status_handler: function(status) {
+ this.status = status;
+ this.redraw("status");
+ },
+ _clusterNodeStats_handler: function(stats) {
+ this.nodeStats = stats;
+ this.redraw("nodeStats");
+ },
+ _clusterNodes_handler: function(nodes) {
+ this.clusterNodes = nodes;
+ this.redraw("clusterNodes");
+ },
+ _clusterHealth_handler: function(health) {
+ this.clusterHealth = health;
+ this.redraw("status");
+ }
+ });
+
+})( this.app );
+(function( $, joey, app ) {
+
+ var ui = app.ns("ui");
+ var ux = app.ns("ux");
+
+ ui.AbstractWidget = ux.Observable.extend({
+ defaults : {
+ id: null // the id of the widget
+ },
+
+ el: null, // this is the jquery wrapped dom element(s) that is the root of the widget
+
+ init: function() {
+ this._super();
+ for(var prop in this) { // automatically bind all the event handlers
+ if(prop.contains("_handler")) {
+ this[prop] = this[prop].bind(this);
+ }
+ }
+ },
+
+ id: function(suffix) {
+ return this.config.id ? (this.config.id + (suffix ? "-" + suffix : "")) : undefined;
+ },
+
+ attach: function( parent, method ) {
+ if( parent ) {
+ this.el[ method || "appendTo"]( parent );
+ }
+ this.fire("attached", this );
+ return this;
+ },
+
+ remove: function() {
+ this.el.remove();
+ this.fire("removed", this );
+ this.removeAllObservers();
+ this.el = null;
+ return this;
+ }
+ });
+
+ joey.plugins.push( function( obj ) {
+ if( obj instanceof ui.AbstractWidget ) {
+ return obj.el[0];
+ }
+ });
+
+})( this.jQuery, this.joey, this.app );
+
+(function( $, app, joey ) {
+
+ var ui = app.ns("ui");
+
+ ui.AbstractField = ui.AbstractWidget.extend({
+
+ defaults: {
+ name : "", // (required) - name of the field
+ require: false, // validation requirements (false, true, regexp, function)
+ value: "", // default value
+ label: "" // human readable label of this field
+ },
+
+ init: function(parent) {
+ this._super();
+ this.el = $.joey(this._main_template());
+ this.field = this.el.find("[name="+this.config.name+"]");
+ this.label = this.config.label;
+ this.require = this.config.require;
+ this.name = this.config.name;
+ this.val( this.config.value );
+ this.attach( parent );
+ },
+
+ val: function( val ) {
+ if(val === undefined) {
+ return this.field.val();
+ } else {
+ this.field.val( val );
+ return this;
+ }
+ },
+
+ validate: function() {
+ var val = this.val(), req = this.require;
+ if( req === false ) {
+ return true;
+ } else if( req === true ) {
+ return val.length > 0;
+ } else if( req.test && $.isFunction(req.test) ) {
+ return req.test( val );
+ } else if( $.isFunction(req) ) {
+ return req( val, this );
+ }
+ }
+
+ });
+
+})( this.jQuery, this.app, this.joey );
+
+(function( app ) {
+
+ var ui = app.ns("ui");
+
+ ui.TextField = ui.AbstractField.extend({
+ init: function() {
+ this._super();
+ },
+ _keyup_handler: function() {
+ this.fire("change", this );
+ },
+ _main_template: function() {
+ return { tag: "DIV", id: this.id(), cls: "uiField uiTextField", children: [
+ { tag: "INPUT",
+ type: "text",
+ name: this.config.name,
+ placeholder: this.config.placeholder,
+ onkeyup: this._keyup_handler
+ }
+ ]};
+ }
+ });
+
+})( this.app );
+
+(function( app ) {
+
+ var ui = app.ns("ui");
+
+ ui.CheckField = ui.AbstractField.extend({
+ _main_template: function() { return (
+ { tag: "DIV", id: this.id(), cls: "uiCheckField", children: [
+ { tag: "INPUT", type: "checkbox", name: this.config.name, checked: !!this.config.value }
+ ] }
+ ); },
+ validate: function() {
+ return this.val() || ( ! this.require );
+ },
+ val: function( val ) {
+ if( val === undefined ) {
+ return !!this.field.attr( "checked" );
+ } else {
+ this.field.attr( "checked", !!val );
+ }
+ }
+ });
+
+})( this.app );
+
+
+
+(function( $, joey, app ) {
+
+ var ui = app.ns("ui");
+
+ ui.Button = ui.AbstractWidget.extend({
+ defaults : {
+ label: "", // the label text
+ disabled: false, // create a disabled button
+ autoDisable: false // automatically disable the button when clicked
+ },
+
+ _baseCls: "uiButton",
+
+ init: function(parent) {
+ this._super();
+ this.el = $.joey(this.button_template())
+ .bind("click", this.click_handler);
+ this.config.disabled && this.disable();
+ this.attach( parent );
+ },
+
+ click_handler: function(jEv) {
+ if(! this.disabled) {
+ this.fire("click", jEv, this);
+ this.config.autoDisable && this.disable();
+ }
+ },
+
+ enable: function() {
+ this.el.removeClass("disabled");
+ this.disabled = false;
+ return this;
+ },
+
+ disable: function(disable) {
+ if(disable === false) {
+ return this.enable();
+ }
+ this.el.addClass("disabled");
+ this.disabled = true;
+ return this;
+ },
+
+ button_template: function() { return (
+ { tag: 'BUTTON', type: 'button', id: this.id(), cls: this._baseCls, children: [
+ { tag: 'DIV', cls: 'uiButton-content', children: [
+ { tag: 'DIV', cls: 'uiButton-label', text: this.config.label }
+ ] }
+ ] }
+ ); }
+ });
+
+})( this.jQuery, this.joey, this.app );
+
+(function( $, app ) {
+
+ var ui = app.ns("ui");
+
+ ui.MenuButton = app.ui.Button.extend({
+ defaults: {
+ menu: null
+ },
+ _baseCls: "uiButton uiMenuButton",
+ init: function(parent) {
+ this._super(parent);
+ this.menu = this.config.menu;
+ this.on("click", this.openMenu_handler);
+ this.menu.on("open", function() { this.el.addClass("active"); }.bind(this));
+ this.menu.on("close", function() { this.el.removeClass("active"); }.bind(this));
+ },
+ openMenu_handler: function(jEv) {
+ this.menu && this.menu.open(jEv);
+ }
+ });
+
+})( this.jQuery, this.app );
+
+(function( $, app ) {
+
+ var ui = app.ns("ui");
+
+ ui.SplitButton = ui.AbstractWidget.extend({
+ defaults: {
+ items: [],
+ label: ""
+ },
+ _baseCls: "uiSplitButton",
+ init: function( parent ) {
+ this._super( parent );
+ this.value = null;
+ this.button = new ui.Button({
+ label: this.config.label,
+ onclick: this._click_handler
+ });
+ this.menu = new ui.SelectMenuPanel({
+ value: this.config.value,
+ items: this._getItems(),
+ onSelect: this._select_handler
+ });
+ this.menuButton = new ui.MenuButton({
+ label: "\u00a0",
+ menu: this.menu
+ });
+ this.el = $.joey(this._main_template());
+ },
+ remove: function() {
+ this.menu.remove();
+ },
+ disable: function() {
+ this.button.disable();
+ },
+ enable: function() {
+ this.button.enable();
+ },
+ _click_handler: function() {
+ this.fire("click", this, { value: this.value } );
+ },
+ _select_handler: function( panel, event ) {
+ this.fire( "select", this, event );
+ },
+ _getItems: function() {
+ return this.config.items;
+ },
+ _main_template: function() {
+ return { tag: "DIV", cls: this._baseCls, children: [
+ this.button, this.menuButton
+ ] };
+ }
+ });
+
+})( this.jQuery, this.app );
+
+(function( $, app, i18n ) {
+
+ var ui = app.ns("ui");
+
+ ui.RefreshButton = ui.SplitButton.extend({
+ defaults: {
+ timer: -1
+ },
+ init: function( parent ) {
+ this.config.label = i18n.text("General.RefreshResults");
+ this._super( parent );
+ this.set( this.config.timer );
+ },
+ set: function( value ) {
+ this.value = value;
+ window.clearInterval( this._timer );
+ if( this.value > 0 ) {
+ this._timer = window.setInterval( this._refresh_handler, this.value );
+ }
+ },
+ _click_handler: function() {
+ this._refresh_handler();
+ },
+ _select_handler: function( el, event ) {
+ this.set( event.value );
+ this.fire("change", this );
+ },
+ _refresh_handler: function() {
+ this.fire("refresh", this );
+ },
+ _getItems: function() {
+ return [
+ { text: i18n.text("General.ManualRefresh"), value: -1 },
+ { text: i18n.text("General.RefreshQuickly"), value: 100 },
+ { text: i18n.text("General.Refresh5seconds"), value: 5000 },
+ { text: i18n.text("General.Refresh1minute"), value: 60000 }
+ ];
+ }
+ });
+
+})( this.jQuery, this.app, this.i18n );
+
+(function( $, app ) {
+
+ var ui = app.ns("ui");
+
+ ui.Toolbar = ui.AbstractWidget.extend({
+ defaults: {
+ label: "",
+ left: [],
+ right: []
+ },
+ init: function(parent) {
+ this._super();
+ this.el = $.joey(this._main_template());
+ },
+ _main_template: function() {
+ return { tag: "DIV", cls: "uiToolbar", children: [
+ { tag: "DIV", cls: "pull-left", children: [
+ { tag: "H2", text: this.config.label }
+ ].concat(this.config.left) },
+ { tag: "DIV", cls: "pull-right", children: this.config.right }
+ ]};
+ }
+ });
+
+})( this.jQuery, this.app );
+
+(function( $, app ) {
+
+ var ui = app.ns("ui");
+
+ ui.AbstractPanel = ui.AbstractWidget.extend({
+ defaults: {
+ body: null, // initial content of the body
+ modal: true, // create a modal panel - creates a div that blocks interaction with page
+ height: 'auto', // panel height
+ width: 400, // panel width (in pixels)
+ open: false, // show the panel when it is created
+ parent: 'BODY', // node that panel is attached to
+ autoRemove: false // remove the panel from the dom and destroy it when the widget is closed
+ },
+ shared: { // shared data for all instances of ui.Panel and decendants
+ stack: [], // array of all open panels
+ modal: $( { tag: "DIV", id: "uiModal", css: { opacity: 0.2, position: "absolute", top: "0px", left: "0px" } } )
+ },
+ init: function() {
+ this._super();
+ },
+ open: function( ev ) {
+ this.el
+ .css( { visibility: "hidden" } )
+ .appendTo( this.config.parent )
+ .css( this._getPosition( ev ) )
+ .css( { zIndex: (this.shared.stack.length ? (+this.shared.stack[this.shared.stack.length - 1].el.css("zIndex") + 10) : 100) } )
+ .css( { visibility: "visible", display: "block" } );
+ this.shared.stack.remove(this);
+ this.shared.stack.push(this);
+ this._setModal();
+ $(document).bind("keyup", this._close_handler );
+ this.fire("open", { source: this, event: ev } );
+ return this;
+ },
+ close: function() {
+ var index = this.shared.stack.indexOf(this);
+ if(index !== -1) {
+ this.shared.stack.splice(index, 1);
+ this.el.css( { left: "-2999px" } ); // move the dialog to the left rather than hiding to prevent ie6 rendering artifacts
+ this._setModal();
+ this.fire("close", this );
+ if(this.config.autoRemove) {
+ this.remove();
+ }
+ }
+ return this;
+ },
+ // close the panel and remove it from the dom, destroying it (you can not reuse the panel after calling remove)
+ remove: function() {
+ this.close();
+ $(document).unbind("keyup", this._close_handler );
+ this._super();
+ },
+ // starting at the top of the stack, find the first panel that wants a modal and put it just underneath, otherwise remove the modal
+ _setModal: function() {
+ for(var stackPtr = this.shared.stack.length - 1; stackPtr >= 0; stackPtr--) {
+ if(this.shared.stack[stackPtr].config.modal) {
+ this.shared.modal
+ .appendTo( document.body )
+ .css( { zIndex: this.shared.stack[stackPtr].el.css("zIndex") - 5 } )
+ .css( $(document).vSize().asSize() );
+ return;
+ }
+ }
+ this.shared.modal.remove(); // no panels that want a modal were found
+ },
+ _getPosition: function() {
+ return $(window).vSize() // get the current viewport size
+ .sub(this.el.vSize()) // subtract the size of the panel
+ .mod(function(s) { return s / 2; }) // divide by 2 (to center it)
+ .add($(document).vScroll()) // add the current scroll offset
+ .mod(function(s) { return Math.max(5, s); }) // make sure the panel is not off the edge of the window
+ .asOffset(); // and return it as a {top, left} object
+ },
+ _close_handler: function( ev ) {
+ if( ev.type === "keyup" && ev.keyCode !== 27) { return; } // press esc key to close
+ $(document).unbind("keyup", this._close_handler);
+ this.close( ev );
+ }
+ });
+
+})( this.jQuery, this.app );
+
+(function( $, app ) {
+
+ var ui = app.ns("ui");
+
+ ui.DraggablePanel = ui.AbstractPanel.extend({
+ defaults: {
+ // title: "" // (required) text for the panel title
+ },
+
+ _baseCls: "uiPanel",
+
+ init: function() {
+ this._super();
+ this.body = $(this._body_template());
+ this.title = $(this._title_template());
+ this.el = $.joey( this._main_template() );
+ this.el.css( { width: this.config.width } );
+ this.dd = new app.ux.DragDrop({
+ pickupSelector: this.el.find(".uiPanel-titleBar"),
+ dragObj: this.el
+ });
+ // open the panel if set in configuration
+ this.config.open && this.open();
+ },
+
+ setBody: function(body) {
+ this.body.empty().append(body);
+ },
+ _body_template: function() { return { tag: "DIV", cls: "uiPanel-body", css: { height: this.config.height + (this.config.height === 'auto' ? "" : "px" ) }, children: [ this.config.body ] }; },
+ _title_template: function() { return { tag: "SPAN", cls: "uiPanel-title", text: this.config.title }; },
+ _main_template: function() { return (
+ { tag: "DIV", id: this.id(), cls: this._baseCls, children: [
+ { tag: "DIV", cls: "uiPanel-titleBar", children: [
+ { tag: "DIV", cls: "uiPanel-close", onclick: this._close_handler, text: "x" },
+ this.title
+ ]},
+ this.body
+ ] }
+ ); }
+ });
+
+})( this.jQuery, this.app );
+
+(function( app ) {
+
+ var ui = app.ns("ui");
+
+ ui.InfoPanel = ui.DraggablePanel.extend({
+ _baseCls: "uiPanel uiInfoPanel"
+ });
+
+})( this.app );
+
+(function( app ) {
+
+ var ui = app.ns("ui");
+
+ ui.DialogPanel = ui.DraggablePanel.extend({
+ _commit_handler: function(jEv) {
+ this.fire("commit", this, { jEv: jEv });
+ },
+ _main_template: function() {
+ var t = this._super();
+ t.children.push(this._actionsBar_template());
+ return t;
+ },
+ _actionsBar_template: function() {
+ return { tag: "DIV", cls: "pull-right", children: [
+ new app.ui.Button({ label: "Cancel", onclick: this._close_handler }),
+ new app.ui.Button({ label: "OK", onclick: this._commit_handler })
+ ]};
+ }
+ });
+
+})( this.app );
+
+(function( app ) {
+
+ var ui = app.ns("ui");
+
+ ui.MenuPanel = ui.AbstractPanel.extend({
+ defaults: {
+ items: [], // (required) an array of menu items
+ modal: false
+ },
+ _baseCls: "uiMenuPanel",
+ init: function() {
+ this._super();
+ this.el = $(this._main_template());
+ },
+ open: function(jEv) {
+ this._super(jEv);
+ var cx = this; setTimeout(function() { $(document).bind("click", cx._close_handler); }, 50);
+ },
+ _getItems: function() {
+ return this.config.items;
+ },
+ _close_handler: function(jEv) {
+ this._super(jEv);
+ $(document).unbind("click", this._close_handler);
+ },
+ _main_template: function() {
+ return { tag: "DIV", cls: this._baseCls, children: this._getItems().map(this._menuItem_template, this) };
+ },
+ _menuItem_template: function(item) {
+ var dx = item.disabled ? { onclick: function() {} } : {};
+ return { tag: "LI", cls: "uiMenuPanel-item" + (item.disabled ? " disabled" : "") + (item.selected ? " selected" : ""), children: [ $.extend({ tag: "DIV", cls: "uiMenuPanel-label" }, item, dx ) ] };
+ },
+ _getPosition: function(jEv) {
+ var right = !! $(jEv.target).parents(".pull-right").length;
+ var parent = $(jEv.target).closest("BUTTON");
+ return parent.vOffset()
+ .addY(parent.vSize().y)
+ .addX( right ? parent.vSize().x - this.el.vOuterSize().x : 0 )
+ .asOffset();
+ }
+ });
+
+})( this.app );
+
+(function( app ) {
+
+ var ui = app.ns("ui");
+
+ ui.SelectMenuPanel = ui.MenuPanel.extend({
+ defaults: {
+ items: [], // (required) an array of menu items
+ value: null
+ },
+ _baseCls: "uiSelectMenuPanel uiMenuPanel",
+ init: function() {
+ this.value = this.config.value;
+ this._super();
+ },
+ _getItems: function() {
+ return this.config.items.map( function( item ) {
+ return {
+ text: item.text,
+ selected: this.value === item.value,
+ onclick: function( jEv ) {
+ var el = $( jEv.target ).closest("LI");
+ el.parent().children().removeClass("selected");
+ el.addClass("selected");
+ this.fire( "select", this, { value: item.value } );
+ this.value = item.value;
+ }.bind(this)
+ };
+ }, this );
+
+ }
+ });
+
+})( this.app );
+
+( function( $, app ) {
+
+ var ui = app.ns("ui");
+
+ ui.Table = ui.AbstractWidget.extend({
+ defaults: {
+ store: null, // (required) implements interface app.data.DataSourceInterface
+ height: 0,
+ width: 0
+ },
+ _baseCls: "uiTable",
+ init: function(parent) {
+ this._super();
+ this.initElements(parent);
+ this.config.store.on("data", this._data_handler);
+ },
+ attach: function(parent) {
+ if(parent) {
+ this._super(parent);
+ this._reflow();
+ }
+ },
+ initElements: function(parent) {
+ this.el = $.joey(this._main_template());
+ this.body = this.el.find(".uiTable-body");
+ this.headers = this.el.find(".uiTable-headers");
+ this.tools = this.el.find(".uiTable-tools");
+ this.attach( parent );
+ },
+ _data_handler: function(store) {
+ this.tools.text(store.summary);
+ this.headers.empty().append(this._header_template(store.columns));
+ this.body.empty().append(this._body_template(store.data, store.columns));
+ this._reflow();
+ },
+ _reflow: function() {
+ var firstCol = this.body.find("TR:first TH.uiTable-header-cell > DIV"),
+ headers = this.headers.find("TR:first TH.uiTable-header-cell > DIV");
+ for(var i = 0; i < headers.length; i++) {
+ $(headers[i]).width( $(firstCol[i]).width() );
+ }
+ this._scroll_handler();
+ },
+ _scroll_handler: function(ev) {
+ this.el.find(".uiTable-headers").scrollLeft(this.body.scrollLeft());
+ },
+ _dataClick_handler: function(ev) {
+ var row = $(ev.target).closest("TR");
+ if(row.length) {
+ this.fire("rowClick", this, { row: row } );
+ }
+ },
+ _headerClick_handler: function(ev) {
+ var header = $(ev.target).closest("TH.uiTable-header-cell");
+ if(header.length) {
+ this.fire("headerClick", this, { header: header, column: header.data("column"), dir: header.data("dir") });
+ }
+ },
+ _main_template: function() {
+ return { tag: "DIV", id: this.id(), css: { width: this.config.width + "px" }, cls: this._baseCls, children: [
+ { tag: "DIV", cls: "uiTable-tools" },
+ { tag: "DIV", cls: "uiTable-headers", onclick: this._headerClick_handler },
+ { tag: "DIV", cls: "uiTable-body",
+ onclick: this._dataClick_handler,
+ onscroll: this._scroll_handler,
+ css: { height: this.config.height + "px", width: this.config.width + "px" }
+ }
+ ] };
+ },
+ _header_template: function(columns) {
+ var ret = { tag: "TABLE", children: [ this._headerRow_template(columns) ] };
+ ret.children[0].children.push(this._headerEndCap_template());
+ return ret;
+ },
+ _headerRow_template: function(columns) {
+ return { tag: "TR", cls: "uiTable-header-row", children: columns.map(function(column) {
+ var dir = ((this.config.store.sort.column === column) && this.config.store.sort.dir) || "none";
+ return { tag: "TH", data: { column: column, dir: dir }, cls: "uiTable-header-cell" + ((dir !== "none") ? " uiTable-sort" : ""), children: [
+ { tag: "DIV", children: [
+ { tag: "DIV", cls: "uiTable-headercell-menu", text: dir === "asc" ? "\u25b2" : "\u25bc" },
+ { tag: "DIV", cls: "uiTable-headercell-text", text: column }
+ ]}
+ ]};
+ }, this)};
+ },
+ _headerEndCap_template: function() {
+ return { tag: "TH", cls: "uiTable-headerEndCap", children: [ { tag: "DIV" } ] };
+ },
+ _body_template: function(data, columns) {
+ return { tag: "TABLE", children: []
+ .concat(this._headerRow_template(columns))
+ .concat(data.map(function(row) {
+ return { tag: "TR", data: { row: row }, cls: "uiTable-row", children: columns.map(function(column){
+ return { tag: "TD", cls: "uiTable-cell", children: [ { tag: "DIV", text: (row[column] || "").toString() } ] };
+ })};
+ }))
+ };
+ }
+
+ });
+
+})( this.jQuery, this.app );
+
+( function( $, app, joey ) {
+
+ var ui = app.ns("ui");
+
+ var CELL_SEPARATOR = ",";
+ var CELL_QUOTE = '"';
+ var LINE_SEPARATOR = "\r\n";
+
+ ui.CSVTable = ui.AbstractWidget.extend({
+ defaults: {
+ results: null
+ },
+ _baseCls: "uiCSVTable",
+ init: function( parent ) {
+ this._super();
+ var results = this.config.results.hits.hits;
+ var columns = this._parseResults( results );
+ this._downloadButton = new ui.Button({
+ label: "Generate Download Link",
+ onclick: this._downloadLinkGenerator_handler
+ });
+ this._downloadLink = $.joey( { tag: "A", text: "download", });
+ this._downloadLink.hide();
+ this._csvText = this._csv_template( columns, results );
+ this.el = $.joey( this._main_template() );
+ this.attach( parent );
+ },
+ _downloadLinkGenerator_handler: function() {
+ this._downloadLink.attr("href", "data:text/csv;chatset=utf-8," + window.encodeURIComponent( this._csvText ) );
+ this._downloadLink.show();
+ },
+ _parseResults: function( results ) {
+ var columnPaths = {};
+ (function parse( path, obj ) {
+ if( obj instanceof Array ) {
+ for( var i = 0; i < obj.length; i++ ) {
+ parse( path, obj[i] );
+ }
+ } else if( typeof obj === "object" ) {
+ for( var prop in obj ) {
+ parse( path + "." + prop, obj[ prop ] );
+ }
+ } else {
+ columnPaths[ path ] = true;
+ }
+ })( "root", results );
+ var columns = [];
+ for( var column in columnPaths ) {
+ columns.push( column.split(".").slice(1) );
+ }
+ return columns;
+ },
+ _main_template: function() { return (
+ { tag: "DIV", cls: this._baseCls, id: this.id(), children: [
+ this._downloadButton,
+ this._downloadLink,
+ { tag: "PRE", text: this._csvText }
+ ] }
+ ); },
+ _csv_template: function( columns, results ) {
+ return this._header_template( columns ) + LINE_SEPARATOR + this._results_template( columns, results );
+ },
+ _header_template: function( columns ) {
+ return columns.map( function( column ) {
+ return column.join(".");
+ }).join( CELL_SEPARATOR );
+ },
+ _results_template: function( columns, results ) {
+ return results.map( function( result ) {
+ return columns.map( function( column ) {
+ var l = 0,
+ ptr = result;
+ while( l !== column.length && ptr != null ) {
+ ptr = ptr[ column[ l++ ] ];
+ }
+ return ( ptr == null ) ? "" : ( CELL_QUOTE + ptr.toString().replace(/"/g, '""') + CELL_QUOTE );
+ }).join( CELL_SEPARATOR );
+ }).join( LINE_SEPARATOR );
+ }
+ });
+
+})( this.jQuery, this.app, this.joey );
+
+(function( $, app ) {
+
+ var ui = app.ns("ui");
+
+ ui.JsonPretty = ui.AbstractWidget.extend({
+ defaults: {
+ obj: null
+ },
+ init: function(parent) {
+ this._super();
+ this.el = $(this._main_template());
+ this.attach(parent);
+ this.el.click(this._click_handler);
+ },
+
+ _click_handler: function(jEv) {
+ var t = $(jEv.target).closest(".uiJsonPretty-name").closest("LI");
+ if(t.length === 0 || t.parents(".uiJsonPretty-minimised").length > 0) { return; }
+ t.toggleClass("uiJsonPretty-minimised");
+ jEv.stopPropagation();
+ },
+
+ _main_template: function() {
+ try {
+ return { tag: "DIV", cls: "uiJsonPretty", children: this.pretty.parse(this.config.obj) };
+ } catch (error) {
+ throw "JsonPretty error: " + error.message;
+ }
+ },
+
+ pretty: { // from https://github.com/RyanAmos/Pretty-JSON/blob/master/pretty_json.js
+ "expando" : function(value) {
+ return (value && (/array|object/i).test(value.constructor.name)) ? "expando" : "";
+ },
+ "parse": function (member) {
+ return this[(member == null) ? 'null' : member.constructor.name.toLowerCase()](member);
+ },
+ "null": function (value) {
+ return this['value']('null', 'null');
+ },
+ "array": function (value) {
+ var results = [];
+ var lastItem = value.length - 1;
+ value.forEach(function( v, i ) {
+ results.push({ tag: "LI", cls: this.expando(v), children: [ this['parse'](v) ] });
+ if( i !== lastItem ) {
+ results.push(",");
+ }
+ }, this);
+ return [ "[ ", ((results.length > 0) ? { tag: "UL", cls: "uiJsonPretty-array", children: results } : null), "]" ];
+ },
+ "object": function (value) {
+ var results = [];
+ var keys = Object.keys( value );
+ var lastItem = keys.length - 1;
+ keys.forEach( function( key, i ) {
+ var children = [ this['value']( 'name', '"' + key + '"' ), ": ", this['parse']( value[ key ]) ];
+ if( i !== lastItem ) {
+ children.push(",");
+ }
+ results.push( { tag: "LI", cls: this.expando( value[ key ] ), children: children } );
+ }, this);
+ return [ "{ ", ((results.length > 0) ? { tag: "UL", cls: "uiJsonPretty-object", children: results } : null ), "}" ];
+ },
+ "number": function (value) {
+ return this['value']('number', value.toString());
+ },
+ "string": function (value) {
+ if (/^(http|https|file):\/\/[^\s]+$/.test(value)) {
+ return this['link']( value );
+ } else {
+ return this['value']('string', '"' + value.toString() + '"');
+ }
+ },
+ "boolean": function (value) {
+ return this['value']('boolean', value.toString());
+ },
+ "link": function( value ) {
+ return this['value']("string", { tag: "A", href: value, target: "_blank", text: '"' + value + '"' } );
+ },
+ "value": function (type, value) {
+ if (/^(http|https|file):\/\/[^\s]+$/.test(value)) {
+ }
+ return { tag: "SPAN", cls: "uiJsonPretty-" + type, text: value };
+ }
+ }
+ });
+
+})( this.jQuery, this.app );
+
+(function( $, app ) {
+
+ var ui = app.ns("ui");
+ var ut = app.ns("ut");
+
+ ui.PanelForm = ui.AbstractWidget.extend({
+ defaults: {
+ fields: null // (required) instanceof app.ux.FieldCollection
+ },
+ init: function(parent) {
+ this._super();
+ this.el = $.joey(this._main_template());
+ this.attach( parent );
+ },
+ _main_template: function() {
+ return { tag: "DIV", id: this.id(), cls: "uiPanelForm", children: this.config.fields.fields.map(this._field_template, this) };
+ },
+ _field_template: function(field) {
+ return { tag: "LABEL", cls: "uiPanelForm-field", children: [
+ { tag: "DIV", cls: "uiPanelForm-label", children: [ field.label, ut.require_template(field) ] },
+ field
+ ]};
+ }
+ });
+
+})( this.jQuery, this.app );
+
+(function( app ){
+
+ var ui = app.ns("ui");
+
+ ui.HelpPanel = ui.InfoPanel.extend({
+ defaults: {
+ ref: "",
+ open: true,
+ autoRemove: true,
+ modal: false,
+ width: 500,
+ height: 450,
+ title: i18n.text("General.Help")
+ },
+ init: function() {
+ this._super();
+ this.body.append(i18n.text(this.config.ref));
+ }
+ });
+
+})( this.app );
+
+(function( app ) {
+
+ var ui = app.ns("ui");
+
+ ui.JsonPanel = ui.InfoPanel.extend({
+ defaults: {
+ json: null, // (required)
+ modal: false,
+ open: true,
+ autoRemove: true,
+ height: 500,
+ width: 600
+ },
+
+ _baseCls: "uiPanel uiInfoPanel uiJsonPanel",
+
+ _body_template: function() {
+ var body = this._super();
+ body.children = [ new ui.JsonPretty({ obj: this.config.json }) ];
+ return body;
+ }
+ });
+
+})( this.app );
+
+(function( $, app, i18n ) {
+
+ var ui = app.ns("ui");
+
+ ui.SidebarSection = ui.AbstractWidget.extend({
+ defaults: {
+ title: "",
+ help: null,
+ body: null,
+ open: false
+ },
+ init: function() {
+ this._super();
+ this.el = $.joey( this._main_template() );
+ this.body = this.el.children(".uiSidebarSection-body");
+ this.config.open && ( this.el.addClass("shown") && this.body.css("display", "block") );
+ },
+ _showSection_handler: function( ev ) {
+ var shown = $( ev.target ).closest(".uiSidebarSection")
+ .toggleClass("shown")
+ .children(".uiSidebarSection-body").slideToggle(200, function() { this.fire("animComplete", this); }.bind(this))
+ .end()
+ .hasClass("shown");
+ this.fire(shown ? "show" : "hide", this);
+ },
+ _showHelp_handler: function( ev ) {
+ new ui.HelpPanel({ref: this.config.help});
+ ev.stopPropagation();
+ },
+ _main_template: function() { return (
+ { tag: "DIV", cls: "uiSidebarSection", children: [
+ (this.config.title && { tag: "DIV", cls: "uiSidebarSection-head", onclick: this._showSection_handler, children: [
+ this.config.title,
+ ( this.config.help && { tag: "SPAN", cls: "uiSidebarSection-help pull-right", onclick: this._showHelp_handler, text: i18n.text("General.HelpGlyph") } )
+ ] }),
+ { tag: "DIV", cls: "uiSidebarSection-body", children: [ this.config.body ] }
+ ] }
+ ); }
+ });
+
+})( this.jQuery, this.app, this.i18n );
+
+(function( $, app ) {
+
+ var ui = app.ns("ui");
+
+ ui.ResultTable = ui.Table.extend({
+ defaults: {
+ width: 500,
+ height: 400
+ },
+
+ init: function() {
+ this._super();
+ this.on("rowClick", this._showPreview_handler);
+ this.selectedRow = null;
+ $(document).bind("keydown", this._nav_handler);
+ },
+ remove: function() {
+ $(document).unbind("keydown", this._nav_handler);
+ this._super();
+ },
+ attach: function(parent) {
+ if(parent) {
+ var height = parent.height() || ( $(document).height() - parent.offset().top - 41 ); // 41 = height in px of .uiTable-tools + uiTable-header
+ var width = parent.width();
+ this.el.width( width );
+ this.body.width( width ).height( height );
+ }
+ this._super(parent);
+ },
+ showPreview: function(row) {
+ row.addClass("selected");
+ this.preview = new app.ui.JsonPanel({
+ title: i18n.text("Browser.ResultSourcePanelTitle"),
+ json: row.data("row")._source,
+ onClose: function() { row.removeClass("selected"); }
+ });
+ },
+ _nav_handler: function(jEv) {
+ if(jEv.keyCode !== 40 && jEv.keyCode !== 38) {
+ return;
+ }
+ this.selectedRow && this.preview && this.preview.remove();
+ if(jEv.keyCode === 40) { // up arrow
+ this.selectedRow = this.selectedRow ? this.selectedRow.next("TR") : this.body.find("TR:first");
+ } else if(jEv.keyCode === 38) { // down arrow
+ this.selectedRow = this.selectedRow ? this.selectedRow.prev("TR") : this.body.find("TR:last");
+ }
+ this.selectedRow && this.showPreview(this.selectedRow);
+ },
+ _showPreview_handler: function(obj, data) {
+ this.showPreview(this.selectedRow = data.row);
+ }
+ });
+
+})( this.jQuery, this.app );
+
+(function( $, app, i18n ) {
+
+ var ui = app.ns("ui");
+ var ut = app.ns("ut");
+
+ ui.QueryFilter = ui.AbstractWidget.extend({
+ defaults: {
+ metadata: null, // (required) instanceof app.data.MetaData
+ query: null // (required) instanceof app.data.Query that the filters will act apon
+ },
+ init: function() {
+ this._super();
+ this.metadata = this.config.metadata;
+ this.query = this.config.query;
+ this.el = $(this._main_template());
+ },
+ helpTypeMap: {
+ "date" : "QueryFilter.DateRangeHelp"
+ },
+ requestUpdate: function(jEv) {
+ if(jEv && jEv.originalEvent) { // we only want to update on real user interaction not generated events
+ this.query.setPage(1);
+ this.query.query();
+ }
+ },
+ getSpec: function(fieldName) {
+ var fieldNameParts = fieldName.split('.');
+ var namePart = 0;
+ var spec = this.metadata.fields[fieldNameParts[namePart]];
+ while (typeof spec.fields !== "undefined") {
+ namePart++;
+ if (typeof spec.fields[fieldNameParts[namePart]] === "undefined") {
+ break;
+ }
+ spec = spec.fields[fieldNameParts[namePart]];
+ }
+ return spec;
+ },
+ _selectAlias_handler: function(jEv) {
+ var indices = (jEv.target.selectedIndex === 0) ? [] : this.metadata.getIndices($(jEv.target).val());
+ $(".uiQueryFilter-index").each(function(i, el) {
+ var jEl = $(el);
+ if(indices.contains(jEl.text()) !== jEl.hasClass("selected")) {
+ jEl.click();
+ }
+ });
+ this.requestUpdate(jEv);
+ },
+ _selectIndex_handler: function(jEv) {
+ var jEl = $(jEv.target).closest(".uiQueryFilter-index");
+ jEl.toggleClass("selected");
+ var selected = jEl.hasClass("selected");
+ this.query.setIndex(jEl.text(), selected);
+ if(selected) {
+ var types = this.metadata.getTypes(this.query.indices);
+ this.el.find("DIV.uiQueryFilter-type.selected").each(function(n, el) {
+ if(! types.contains($(el).text())) {
+ $(el).click();
+ }
+ });
+ }
+ this.requestUpdate(jEv);
+ },
+ _selectType_handler: function(jEv) {
+ var jEl = $(jEv.target).closest(".uiQueryFilter-type");
+ jEl.toggleClass("selected");
+ var type = jEl.text(), selected = jEl.hasClass("selected");
+ this.query.setType(type, selected);
+ if(selected) {
+ var indices = this.metadata.types[type].indices;
+ // es throws a 500 if searching an index for a type it does not contain - so we prevent that
+ this.el.find("DIV.uiQueryFilter-index.selected").each(function(n, el) {
+ if(! indices.contains($(el).text())) {
+ $(el).click();
+ }
+ });
+ // es throws a 500 if you specify types from different indices with _all
+ jEl.siblings(".uiQueryFilter-type.selected").forEach(function(el) {
+ if(this.metadata.types[$(el).text()].indices.intersection(indices).length === 0) {
+ $(el).click();
+ }
+ }, this);
+ }
+ this.requestUpdate(jEv);
+ },
+ _openFilter_handler: function(section) {
+ var field_name = section.config.title;
+ if(! section.loaded) {
+ var spec = this.getSpec(field_name);
+ if(spec.core_type === "string") {
+ section.body.append(this._textFilter_template(spec));
+ } else if(spec.core_type === "date") {
+ section.body.append(this._dateFilter_template(spec));
+ section.body.append(new ui.DateHistogram({ printEl: section.body.find("INPUT"), cluster: this.cluster, query: this.query, spec: spec }));
+ } else if(spec.core_type === "number") {
+ section.body.append(this._numericFilter_template(spec));
+ } else if(spec.core_type === 'boolean') {
+ section.body.append(this._booleanFilter_template(spec));
+ } else if (spec.core_type === 'multi_field') {
+ section.body.append(this._multiFieldFilter_template(section, spec));
+ }
+ section.loaded = true;
+ }
+ section.on("animComplete", function(section) { section.body.find("INPUT").focus(); });
+ },
+ _textFilterChange_handler: function(jEv) {
+ var jEl = $(jEv.target).closest("INPUT");
+ var val = jEl.val();
+ var spec = jEl.data("spec");
+ var uqids = jEl.data("uqids") || [];
+ uqids.forEach(function(uqid) {
+ uqid && this.query.removeClause(uqid);
+ }, this);
+ if(val.length) {
+ if(jEl[0] === document.activeElement && jEl[0].selectionStart === jEl[0].selectionEnd) {
+ val = val.replace(new RegExp("(.{"+jEl[0].selectionStart+"})"), "$&*");
+ }
+ uqids = val.split(/\s+/).map(function(term) {
+ // Figure out the actual field name - needed for multi_field, because
+ // querying for "field.field" will not work. Simply "field" must be used
+ // if nothing is aliased.
+ var fieldNameParts = spec.field_name.split('.');
+ var part = fieldNameParts.length - 1;
+ var name = fieldNameParts[part];
+ while (part >= 1) {
+ if (fieldNameParts[part] !== fieldNameParts[part - 1]) {
+ name = fieldNameParts[part - 1] + "." + name;
+ }
+ part--;
+ }
+ return term && this.query.addClause(term, name, "wildcard", "must");
+ }, this);
+ }
+ jEl.data("uqids", uqids);
+ this.requestUpdate(jEv);
+ },
+ _dateFilterChange_handler: function(jEv) {
+ var jEl = $(jEv.target).closest("INPUT");
+ var val = jEl.val();
+ var spec = jEl.data("spec");
+ var uqid = jEl.data("uqid") || null;
+ var range = window.dateRangeParser.parse(val);
+ var lastRange = jEl.data("lastRange");
+ if(!range || (lastRange && lastRange.start === range.start && lastRange.end === range.end)) {
+ return;
+ }
+ uqid && this.query.removeClause(uqid);
+ if((range.start && range.end) === null) {
+ uqid = null;
+ } else {
+ var value = {};
+ if( range.start ) {
+ value["gte"] = range.start;
+ }
+ if( range.end ) {
+ value["lte"] = range.end;
+ }
+ uqid = this.query.addClause( value, spec.field_name, "range", "must");
+ }
+ jEl.data("lastRange", range);
+ jEl.siblings(".uiQueryFilter-rangeHintFrom")
+ .text(i18n.text("QueryFilter.DateRangeHint.from", range.start && new Date(range.start).toUTCString()));
+ jEl.siblings(".uiQueryFilter-rangeHintTo")
+ .text(i18n.text("QueryFilter.DateRangeHint.to", range.end && new Date(range.end).toUTCString()));
+ jEl.data("uqid", uqid);
+ this.requestUpdate(jEv);
+ },
+ _numericFilterChange_handler: function(jEv) {
+ var jEl = $(jEv.target).closest("INPUT");
+ var val = jEl.val();
+ var spec = jEl.data("spec");
+ var uqid = jEl.data("uqid") || null;
+ var lastRange = jEl.data("lastRange");
+ var range = (function(val) {
+ var ops = val.split(/->|<>|</).map( function(v) { return parseInt(v.trim(), 10); });
+ if(/<>/.test(val)) {
+ return { gte: (ops[0] - ops[1]), lte: (ops[0] + ops[1]) };
+ } else if(/->|</.test(val)) {
+ return { gte: ops[0], lte: ops[1] };
+ } else {
+ return { gte: ops[0], lte: ops[0] };
+ }
+ })(val || "");
+ if(!range || (lastRange && lastRange.lte === range.lte && lastRange.gte === range.gte)) {
+ return;
+ }
+ jEl.data("lastRange", range);
+ uqid && this.query.removeClause(uqid);
+ uqid = this.query.addClause( range, spec.field_name, "range", "must");
+ jEl.data("uqid", uqid);
+ this.requestUpdate(jEv);
+ },
+ _booleanFilterChange_handler: function( jEv ) {
+ var jEl = $(jEv.target).closest("SELECT");
+ var val = jEl.val();
+ var spec = jEl.data("spec");
+ var uqid = jEl.data("uqid") || null;
+ uqid && this.query.removeClause(uqid);
+ if(val === "true" || val === "false") {
+ jEl.data("uqid", this.query.addClause(val, spec.field_name, "term", "must") );
+ }
+ this.requestUpdate(jEv);
+ },
+ _main_template: function() {
+ return { tag: "DIV", id: this.id(), cls: "uiQueryFilter", children: [
+ this._aliasSelector_template(),
+ this._indexSelector_template(),
+ this._typesSelector_template(),
+ this._filters_template()
+ ] };
+ },
+ _aliasSelector_template: function() {
+ var aliases = Object.keys(this.metadata.aliases).sort();
+ aliases.unshift( i18n.text("QueryFilter.AllIndices") );
+ return { tag: "DIV", cls: "uiQueryFilter-section uiQueryFilter-aliases", children: [
+ { tag: "SELECT", onChange: this._selectAlias_handler, children: aliases.map(ut.option_template) }
+ ] };
+ },
+ _indexSelector_template: function() {
+ var indices = Object.keys( this.metadata.indices ).sort();
+ return { tag: "DIV", cls: "uiQueryFilter-section uiQueryFilter-indices", children: [
+ { tag: "HEADER", text: i18n.text("QueryFilter-Header-Indices") },
+ { tag: "DIV", onClick: this._selectIndex_handler, children: indices.map( function( name ) {
+ return { tag: "DIV", cls: "uiQueryFilter-booble uiQueryFilter-index", text: name };
+ })}
+ ] };
+ },
+ _typesSelector_template: function() {
+ var types = Object.keys( this.metadata.types ).sort();
+ return { tag: "DIV", cls: "uiQueryFilter-section uiQueryFilter-types", children: [
+ { tag: "HEADER", text: i18n.text("QueryFilter-Header-Types") },
+ { tag: "DIV", onClick: this._selectType_handler, children: types.map( function( name ) {
+ return { tag: "DIV", cls: "uiQueryFilter-booble uiQueryFilter-type", text: name };
+ })}
+ ] };
+ },
+ _filters_template: function() {
+ var fields = Object.keys( this.metadata.fields ).sort();
+ return { tag: "DIV", cls: "uiQueryFilter-section uiQueryFilter-filters", children: [
+ { tag: "HEADER", text: i18n.text("QueryFilter-Header-Fields") },
+ { tag: "DIV", children: fields.map( function(name ) {
+ return new app.ui.SidebarSection({
+ title: name,
+ help: this.helpTypeMap[this.metadata.fields[ name ].type],
+ onShow: this._openFilter_handler
+ });
+ }, this ) }
+ ] };
+ },
+ _textFilter_template: function(spec) {
+ return { tag: "INPUT", data: { spec: spec }, onKeyup: this._textFilterChange_handler };
+ },
+ _dateFilter_template: function(spec) {
+ return { tag: "DIV", children: [
+ { tag: "INPUT", data: { spec: spec }, onKeyup: this._dateFilterChange_handler },
+ { tag: "PRE", cls: "uiQueryFilter-rangeHintFrom", text: i18n.text("QueryFilter.DateRangeHint.from", "")},
+ { tag: "PRE", cls: "uiQueryFilter-rangeHintTo", text: i18n.text("QueryFilter.DateRangeHint.to", "") }
+ ]};
+ },
+ _numericFilter_template: function(spec) {
+ return { tag: "INPUT", data: { spec: spec }, onKeyup: this._numericFilterChange_handler };
+ },
+ _booleanFilter_template: function(spec) {
+ return { tag: "SELECT", data: { spec: spec }, onChange: this._booleanFilterChange_handler,
+ children: [ i18n.text("QueryFilter.AnyValue"), "true", "false" ].map( function( val ) {
+ return { tag: "OPTION", value: val, text: val };
+ })
+ };
+ },
+ _multiFieldFilter_template: function(section, spec) {
+ return {
+ tag : "DIV", cls : "uiQueryFilter-subMultiFields", children : acx.eachMap(spec.fields, function(name, data) {
+ if (name === spec.field_name) {
+ section.config.title = spec.field_name + "." + name;
+ return this._openFilter_handler(section);
+ }
+ return new app.ui.SidebarSection({
+ title : data.field_name, help : this.helpTypeMap[data.type], onShow : this._openFilter_handler
+ });
+ }, this)
+ };
+ }
+ });
+
+})( this.jQuery, this.app, this.i18n );
+
+(function( app ) {
+
+ var ui = app.ns("ui");
+
+ ui.Page = ui.AbstractWidget.extend({
+ show: function() {
+ this.el.show();
+ },
+ hide: function() {
+ this.el.hide();
+ }
+ });
+
+})( this.app );
+(function( $, app, i18n ){
+
+ var ui = app.ns("ui");
+ var data = app.ns("data");
+
+ ui.Browser = ui.Page.extend({
+ defaults: {
+ cluster: null // (required) instanceof app.services.Cluster
+ },
+ init: function() {
+ this._super();
+ this.cluster = this.config.cluster;
+ this.query = new app.data.Query( { cluster: this.cluster } );
+ this._refreshButton = new ui.Button({
+ label: i18n.text("General.RefreshResults"),
+ onclick: function( btn ) {
+ this.query.query();
+ }.bind(this)
+ });
+ this.el = $(this._main_template());
+ new data.MetaDataFactory({
+ cluster: this.cluster,
+ onReady: function(metadata) {
+ this.metadata = metadata;
+ this.store = new data.QueryDataSourceInterface( { metadata: metadata, query: this.query } );
+ this.queryFilter = new ui.QueryFilter({ metadata: metadata, query: this.query });
+ this.queryFilter.attach(this.el.find("> .uiBrowser-filter") );
+ this.resultTable = new ui.ResultTable( {
+ onHeaderClick: this._changeSort_handler,
+ store: this.store
+ } );
+ this.resultTable.attach( this.el.find("> .uiBrowser-table") );
+ this.updateResults();
+ }.bind(this)
+ });
+ },
+ updateResults: function() {
+ this.query.query();
+ },
+ _changeSort_handler: function(table, wEv) {
+ this.query.setSort(wEv.column, wEv.dir === "desc");
+ this.query.setPage(1);
+ this.query.query();
+ },
+ _main_template: function() {
+ return { tag: "DIV", cls: "uiBrowser", children: [
+ new ui.Toolbar({
+ label: i18n.text("Browser.Title"),
+ left: [ ],
+ right: [ this._refreshButton ]
+ }),
+ { tag: "DIV", cls: "uiBrowser-filter" },
+ { tag: "DIV", cls: "uiBrowser-table" }
+ ] };
+ }
+ });
+
+})( this.jQuery, this.app, this.i18n );
+
+(function( $, app, i18n, raphael ) {
+
+ var ui = app.ns("ui");
+ var ut = app.ns("ut");
+ var services = app.ns("services");
+
+ ui.AnyRequest = ui.Page.extend({
+ defaults: {
+ cluster: null, // (required) instanceof app.services.Cluster
+ path: "_search", // default uri to send a request to
+ query: { query: { match_all: { }}},
+ transform: " return root;" // default transformer function (does nothing)
+ },
+ init: function(parent) {
+ this._super();
+ this.prefs = services.Preferences.instance();
+ this.history = this.prefs.get("anyRequest-history") || [ { type: "POST", path: this.config.path, query : JSON.stringify(this.config.query), transform: this.config.transform } ];
+ this.el = $.joey(this._main_template());
+ this.base_uriEl = this.el.find("INPUT[name=base_uri]");
+ this.pathEl = this.el.find("INPUT[name=path]");
+ this.typeEl = this.el.find("SELECT[name=method]");
+ this.dataEl = this.el.find("TEXTAREA[name=body]");
+ this.prettyEl = this.el.find("INPUT[name=pretty]");
+ this.transformEl = this.el.find("TEXTAREA[name=transform]");
+ this.asGraphEl = this.el.find("INPUT[name=asGraph]");
+ this.asTableEl = this.el.find("INPUT[name=asTable]");
+ this.asJsonEl = this.el.find("INPUT[name=asJson]");
+ this.cronEl = this.el.find("SELECT[name=cron]");
+ this.outEl = this.el.find("DIV.uiAnyRequest-out");
+ this.errEl = this.el.find("DIV.uiAnyRequest-jsonErr");
+ this.typeEl.val("GET");
+ this.attach(parent);
+ this.setHistoryItem(this.history[this.history.length - 1]);
+ },
+ setHistoryItem: function(item) {
+ this.pathEl.val(item.path);
+ this.typeEl.val(item.type);
+ this.dataEl.val(item.query);
+ this.transformEl.val(item.transform);
+ },
+ _request_handler: function( ev ) {
+ if(! this._validateJson_handler()) {
+ return;
+ }
+ var path = this.pathEl.val(),
+ type = this.typeEl.val(),
+ query = JSON.stringify(JSON.parse(this.dataEl.val())),
+ transform = this.transformEl.val(),
+ base_uri = this.base_uriEl.val();
+ if( ev ) { // if the user click request
+ if(this.timer) {
+ window.clearTimeout(this.timer); // stop any cron jobs
+ }
+ delete this.prevData; // remove data from previous cron runs
+ this.outEl.text(i18n.text("AnyRequest.Requesting"));
+ if( ! /\/$/.test( base_uri )) {
+ base_uri += "/";
+ this.base_uriEl.val( base_uri );
+ }
+ for(var i = 0; i < this.history.length; i++) {
+ if(this.history[i].path === path &&
+ this.history[i].type === type &&
+ this.history[i].query === query &&
+ this.history[i].transform === transform) {
+ this.history.splice(i, 1);
+ }
+ }
+ this.history.push({
+ path: path,
+ type: type,
+ query: query,
+ transform: transform
+ });
+ this.history.slice(250); // make sure history does not get too large
+ this.prefs.set( "anyRequest-history", this.history );
+ this.el.find("UL.uiAnyRequest-history")
+ .empty()
+ .append($( { tag: "UL", children: this.history.map(this._historyItem_template, this) }).children())
+ .children().find(":last-child").each(function(i, j) { j.scrollIntoView(false); }).end()
+ .scrollLeft(0);
+ }
+ this.config.cluster.request({
+ url: base_uri + path,
+ type: type,
+ data: query,
+ success: this._responseWriter_handler,
+ error: this._responseError_handler
+ });
+ },
+ _responseError_handler: function (response) {
+ var obj;
+ try {
+ obj = JSON.parse(response.responseText);
+ if (obj) {
+ this._responseWriter_handler(obj);
+ }
+ } catch (err) {
+ }
+ },
+ _responseWriter_handler: function(data) {
+ this.outEl.empty();
+ try {
+ data = (new Function("root", "prev", this.transformEl.val
<TRUNCATED>
http://git-wip-us.apache.org/repos/asf/karaf-decanter/blob/da345201/elasticsearch-head-1.x/src/main/resources/base/favicon.png
----------------------------------------------------------------------
diff --git a/elasticsearch-head-1.x/src/main/resources/base/favicon.png b/elasticsearch-head-1.x/src/main/resources/base/favicon.png
new file mode 100644
index 0000000..f433ec6
Binary files /dev/null and b/elasticsearch-head-1.x/src/main/resources/base/favicon.png differ