<commit_before>/**
 * Visual Blocks Language
 *
 * Copyright 2012 Google Inc.
 * http://code.google.com/p/blockly/
 *
 * 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.
 */

/**
 * @fileoverview Helper functions for generating JavaScript for blocks.
 * @author fraser@google.com (Neil Fraser)
 */
'use strict';

Blockly.JavaScript = Blockly.Generator.get('JavaScript');

/**
 * List of illegal variable names.
 * This is not intended to be a security feature.  Blockly is 100% client-side,
 * so bypassing this list is trivial.  This is intended to prevent users from
 * accidentally clobbering a built-in object or function.
 * @private
 */
if (!Blockly.JavaScript.RESERVED_WORDS_) {
  Blockly.JavaScript.RESERVED_WORDS_ = '';
}

Blockly.JavaScript.RESERVED_WORDS_ +=
    'Blockly', // In case JS is evaled in the current window.
    // https://developer.mozilla.org/en/JavaScript/Reference/Reserved_Words
    'break,case,catch,continue,debugger,default,delete,do,else,finally,for,function,if,in,instanceof,new,return,switch,this,throw,try,typeof,var,void,while,with,' +
    'class,enum,export,extends,import,super,implements,interface,let,package,private,protected,public,static,yield,' +
    'const,null,true,false,' +
    // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects
    'Array,ArrayBuffer,Boolean,Date,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,Error,eval,EvalError,Float32Array,Float64Array,Function,Infinity,Int16Array,Int32Array,Int8Array,isFinite,isNaN,Iterator,JSON,Math,NaN,Number,Object,parseFloat,parseInt,RangeError,ReferenceError,RegExp,StopIteration,String,SyntaxError,TypeError,Uint16Array,Uint32Array,Uint8Array,Uint8ClampedArray,undefined,uneval,URIError,' +
    // https://developer.mozilla.org/en/DOM/window
    'applicationCache,closed,Components,content,_content,controllers,crypto,defaultStatus,dialogArguments,directories,document,frameElement,frames,fullScreen,globalStorage,history,innerHeight,innerWidth,length,location,locationbar,localStorage,menubar,messageManager,mozAnimationStartTime,mozInnerScreenX,mozInnerScreenY,mozPaintCount,name,navigator,opener,outerHeight,outerWidth,pageXOffset,pageYOffset,parent,performance,personalbar,pkcs11,returnValue,screen,screenX,screenY,scrollbars,scrollMaxX,scrollMaxY,scrollX,scrollY,self,sessionStorage,sidebar,status,statusbar,toolbar,top,URL,window,' +
    'addEventListener,alert,atob,back,blur,btoa,captureEvents,clearImmediate,clearInterval,clearTimeout,close,confirm,disableExternalCapture,dispatchEvent,dump,enableExternalCapture,escape,find,focus,forward,GeckoActiveXObject,getAttention,getAttentionWithCycleCount,getComputedStyle,getSelection,home,matchMedia,maximize,minimize,moveBy,moveTo,mozRequestAnimationFrame,open,openDialog,postMessage,print,prompt,QueryInterface,releaseEvents,removeEventListener,resizeBy,resizeTo,restore,routeEvent,scroll,scrollBy,scrollByLines,scrollByPages,scrollTo,setCursor,setImmediate,setInterval,setResizable,setTimeout,showModalDialog,sizeToContent,stop,unescape,updateCommands,XPCNativeWrapper,XPCSafeJSObjectWrapper,' +
    'onabort,onbeforeunload,onblur,onchange,onclick,onclose,oncontextmenu,ondevicemotion,ondeviceorientation,ondragdrop,onerror,onfocus,onhashchange,onkeydown,onkeypress,onkeyup,onload,onmousedown,onmousemove,onmouseout,onmouseover,onmouseup,onmozbeforepaint,onpaint,onpopstate,onreset,onresize,onscroll,onselect,onsubmit,onunload,onpageshow,onpagehide,' +
    'Image,Option,Worker,' +
    // https://developer.mozilla.org/en/Gecko_DOM_Reference
    'Event,Range,File,FileReader,Blob,BlobBuilder,' +
    'Attr,CDATASection,CharacterData,Comment,console,DocumentFragment,DocumentType,DomConfiguration,DOMError,DOMErrorHandler,DOMException,DOMImplementation,DOMImplementationList,DOMImplementationRegistry,DOMImplementationSource,DOMLocator,DOMObject,DOMString,DOMStringList,DOMTimeStamp,DOMUserData,Entity,EntityReference,MediaQueryList,MediaQueryListListener,NameList,NamedNodeMap,Node,NodeFilter,NodeIterator,NodeList,Notation,Plugin,PluginArray,ProcessingInstruction,SharedWorker,Text,TimeRanges,Treewalker,TypeInfo,UserDataHandler,Worker,WorkerGlobalScope,' +
    'HTMLDocument,HTMLElement,HTMLAnchorElement,HTMLAppletElement,HTMLAudioElement,HTMLAreaElement,HTMLBaseElement,HTMLBaseFontElement,HTMLBodyElement,HTMLBRElement,HTMLButtonElement,HTMLCanvasElement,HTMLDirectoryElement,HTMLDivElement,HTMLDListElement,HTMLEmbedElement,HTMLFieldSetElement,HTMLFontElement,HTMLFormElement,HTMLFrameElement,HTMLFrameSetElement,HTMLHeadElement,HTMLHeadingElement,HTMLHtmlElement,HTMLHRElement,HTMLIFrameElement,HTMLImageElement,HTMLInputElement,HTMLKeygenElement,HTMLLabelElement,HTMLLIElement,HTMLLinkElement,HTMLMapElement,HTMLMenuElement,HTMLMetaElement,HTMLModElement,HTMLObjectElement,HTMLOListElement,HTMLOptGroupElement,HTMLOptionElement,HTMLOutputElement,HTMLParagraphElement,HTMLParamElement,HTMLPreElement,HTMLQuoteElement,HTMLScriptElement,HTMLSelectElement,HTMLSourceElement,HTMLSpanElement,HTMLStyleElement,HTMLTableElement,HTMLTableCaptionElement,HTMLTableCellElement,HTMLTableDataCellElement,HTMLTableHeaderCellElement,HTMLTableColElement,HTMLTableRowElement,HTMLTableSectionElement,HTMLTextAreaElement,HTMLTimeElement,HTMLTitleElement,HTMLTrackElement,HTMLUListElement,HTMLUnknownElement,HTMLVideoElement,' +
    'HTMLCanvasElement,CanvasRenderingContext2D,CanvasGradient,CanvasPattern,TextMetrics,ImageData,CanvasPixelArray,HTMLAudioElement,HTMLVideoElement,NotifyAudioAvailableEvent,HTMLCollection,HTMLAllCollection,HTMLFormControlsCollection,HTMLOptionsCollection,HTMLPropertiesCollection,DOMTokenList,DOMSettableTokenList,DOMStringMap,RadioNodeList,' +
    'SVGDocument,SVGElement,SVGAElement,SVGAltGlyphElement,SVGAltGlyphDefElement,SVGAltGlyphItemElement,SVGAnimationElement,SVGAnimateElement,SVGAnimateColorElement,SVGAnimateMotionElement,SVGAnimateTransformElement,SVGSetElement,SVGCircleElement,SVGClipPathElement,SVGColorProfileElement,SVGCursorElement,SVGDefsElement,SVGDescElement,SVGEllipseElement,SVGFilterElement,SVGFilterPrimitiveStandardAttributes,SVGFEBlendElement,SVGFEColorMatrixElement,SVGFEComponentTransferElement,SVGFECompositeElement,SVGFEConvolveMatrixElement,SVGFEDiffuseLightingElement,SVGFEDisplacementMapElement,SVGFEDistantLightElement,SVGFEFloodElement,SVGFEGaussianBlurElement,SVGFEImageElement,SVGFEMergeElement,SVGFEMergeNodeElement,SVGFEMorphologyElement,SVGFEOffsetElement,SVGFEPointLightElement,SVGFESpecularLightingElement,SVGFESpotLightElement,SVGFETileElement,SVGFETurbulenceElement,SVGComponentTransferFunctionElement,SVGFEFuncRElement,SVGFEFuncGElement,SVGFEFuncBElement,SVGFEFuncAElement,SVGFontElement,SVGFontFaceElement,SVGFontFaceFormatElement,SVGFontFaceNameElement,SVGFontFaceSrcElement,SVGFontFaceUriElement,SVGForeignObjectElement,SVGGElement,SVGGlyphElement,SVGGlyphRefElement,SVGGradientElement,SVGLinearGradientElement,SVGRadialGradientElement,SVGHKernElement,SVGImageElement,SVGLineElement,SVGMarkerElement,SVGMaskElement,SVGMetadataElement,SVGMissingGlyphElement,SVGMPathElement,SVGPathElement,SVGPatternElement,SVGPolylineElement,SVGPolygonElement,SVGRectElement,SVGScriptElement,SVGStopElement,SVGStyleElement,SVGSVGElement,SVGSwitchElement,SVGSymbolElement,SVGTextElement,SVGTextPathElement,SVGTitleElement,SVGTRefElement,SVGTSpanElement,SVGUseElement,SVGViewElement,SVGVKernElement,' +
    'SVGAngle,SVGColor,SVGICCColor,SVGElementInstance,SVGElementInstanceList,SVGLength,SVGLengthList,SVGMatrix,SVGNumber,SVGNumberList,SVGPaint,SVGPoint,SVGPointList,SVGPreserveAspectRatio,SVGRect,SVGStringList,SVGTransform,SVGTransformList,' +
    'SVGAnimatedAngle,SVGAnimatedBoolean,SVGAnimatedEnumeration,SVGAnimatedInteger,SVGAnimatedLength,SVGAnimatedLengthList,SVGAnimatedNumber,SVGAnimatedNumberList,SVGAnimatedPreserveAspectRatio,SVGAnimatedRect,SVGAnimatedString,SVGAnimatedTransformList,' +
    'SVGPathSegList,SVGPathSeg,SVGPathSegArcAbs,SVGPathSegArcRel,SVGPathSegClosePath,SVGPathSegCurvetoCubicAbs,SVGPathSegCurvetoCubicRel,SVGPathSegCurvetoCubicSmoothAbs,SVGPathSegCurvetoCubicSmoothRel,SVGPathSegCurvetoQuadraticAbs,SVGPathSegCurvetoQuadraticRel,SVGPathSegCurvetoQuadraticSmoothAbs,SVGPathSegCurvetoQuadraticSmoothRel,SVGPathSegLinetoAbs,SVGPathSegLinetoHorizontalAbs,SVGPathSegLinetoHorizontalRel,SVGPathSegLinetoRel,SVGPathSegLinetoVerticalAbs,SVGPathSegLinetoVerticalRel,SVGPathSegMovetoAbs,SVGPathSegMovetoRel,ElementTimeControl,TimeEvent,SVGAnimatedPathData,' +
    'SVGAnimatedPoints,SVGColorProfileRule,SVGCSSRule,SVGExternalResourcesRequired,SVGFitToViewBox,SVGLangSpace,SVGLocatable,SVGRenderingIntent,SVGStylable,SVGTests,SVGTextContentElement,SVGTextPositioningElement,SVGTransformable,SVGUnitTypes,SVGURIReference,SVGViewSpec,SVGZoomAndPan,';

/**
 * Order of operation ENUMs.
 * https://developer.mozilla.org/en/JavaScript/Reference/Operators/Operator_Precedence
 */
Blockly.JavaScript.ORDER_ATOMIC = 0;         // 0 "" ...
Blockly.JavaScript.ORDER_MEMBER = 1;         // . []
Blockly.JavaScript.ORDER_NEW = 1;            // new
Blockly.JavaScript.ORDER_FUNCTION_CALL = 2;  // ()
Blockly.JavaScript.ORDER_INCREMENT = 3;      // ++
Blockly.JavaScript.ORDER_DECREMENT = 3;      // --
Blockly.JavaScript.ORDER_LOGICAL_NOT = 4;    // !
Blockly.JavaScript.ORDER_BITWISE_NOT = 4;    // ~
Blockly.JavaScript.ORDER_UNARY_PLUS = 4;     // +
Blockly.JavaScript.ORDER_UNARY_NEGATION = 4; // -
Blockly.JavaScript.ORDER_TYPEOF = 4;         // typeof
Blockly.JavaScript.ORDER_VOID = 4;           // void
Blockly.JavaScript.ORDER_DELETE = 4;         // delete
Blockly.JavaScript.ORDER_MULTIPLICATION = 5; // *
Blockly.JavaScript.ORDER_DIVISION = 5;       // /
Blockly.JavaScript.ORDER_MODULUS = 5;        // %
Blockly.JavaScript.ORDER_ADDITION = 6;       // +
Blockly.JavaScript.ORDER_SUBTRACTION = 6;    // -
Blockly.JavaScript.ORDER_BITWISE_SHIFT = 7;  // << >> >>>
Blockly.JavaScript.ORDER_RELATIONAL = 8;     // < <= > >=
Blockly.JavaScript.ORDER_IN = 8;             // in
Blockly.JavaScript.ORDER_INSTANCEOF = 8;     // instanceof
Blockly.JavaScript.ORDER_EQUALITY = 9;       // == != === !==
Blockly.JavaScript.ORDER_BITWISE_AND = 10;   // &
Blockly.JavaScript.ORDER_BITWISE_XOR = 11;   // ^
Blockly.JavaScript.ORDER_BITWISE_OR = 12;    // |
Blockly.JavaScript.ORDER_LOGICAL_AND = 13;   // &&
Blockly.JavaScript.ORDER_LOGICAL_OR = 14;    // ||
Blockly.JavaScript.ORDER_CONDITIONAL = 15;   // ?:
Blockly.JavaScript.ORDER_ASSIGNMENT = 16;    // = += -= *= /= %= <<= >>= ...
Blockly.JavaScript.ORDER_COMMA = 17;         // ,
Blockly.JavaScript.ORDER_NONE = 99;          // (...)

/**
 * Initialise the database of variable names.
 */
Blockly.JavaScript.init = function() {
  // Create a dictionary of definitions to be printed before the code.
  Blockly.JavaScript.definitions_ = {};

  if (Blockly.Variables) {
    if (!Blockly.JavaScript.variableDB_) {
      Blockly.JavaScript.variableDB_ =
          new Blockly.Names(Blockly.JavaScript.RESERVED_WORDS_);
    } else {
      Blockly.JavaScript.variableDB_.reset();
    }

    var defvars = [];
    var variables = Blockly.Variables.allVariables();
    for (var x = 0; x < variables.length; x++) {
      defvars[x] = 'var ' +
          Blockly.JavaScript.variableDB_.getDistinctName(variables[x],
          Blockly.Variables.NAME_TYPE) + ';';
    }
    Blockly.JavaScript.definitions_['variables'] = defvars.join('\n');
  }
};

/**
 * Prepend the generated code with the variable definitions.
 * @param {string} code Generated code.
 * @return {string} Completed code.
 */
Blockly.JavaScript.finish = function(code) {
  // Convert the definitions dictionary into a list.
  var definitions = [];
  for (var name in Blockly.JavaScript.definitions_) {
    definitions.push(Blockly.JavaScript.definitions_[name]);
  }
  return definitions.join('\n\n') + '\n\n\n' + code;
};

/**
 * Naked values are top-level blocks with outputs that aren't plugged into
 * anything.  A trailing semicolon is needed to make this legal.
 * @param {string} line Line of generated code.
 * @return {string} Legal line of code.
 */
Blockly.JavaScript.scrubNakedValue = function(line) {
  return line + ';\n';
};

/**
 * Encode a string as a properly escaped JavaScript string, complete with
 * quotes.
 * @param {string} string Text to encode.
 * @return {string} JavaScript string.
 * @private
 */
Blockly.JavaScript.quote_ = function(string) {
  // TODO: This is a quick hack.  Replace with goog.string.quote
  string = string.replace(/\\/g, '\\\\')
                 .replace(/\n/g, '\\\n')
                 .replace(/'/g, '\\\'');
  return '\'' + string + '\'';
};

/**
 * Common tasks for generating JavaScript from blocks.
 * Handles comments for the specified block and any connected value blocks.
 * Calls any statements following this block.
 * @param {!Blockly.Block} block The current block.
 * @param {string} code The JavaScript code created for this block.
 * @return {string} JavaScript code with comments and subsequent blocks added.
 * @this {Blockly.CodeGenerator}
 * @private
 */
Blockly.JavaScript.scrub_ = function(block, code) {
  if (code === null) {
    // Block has handled code generation itself.
    return '';
  }
  var commentCode = '';
  // Only collect comments for blocks that aren't inline.
  if (!block.outputConnection || !block.outputConnection.targetConnection) {
    // Collect comment for this block.
    var comment = block.getCommentText();
    if (comment) {
      commentCode += Blockly.Generator.prefixLines(comment, '// ') + '\n';
    }
    // Collect comments for all value arguments.
    // Don't collect comments for nested statements.
    for (var x = 0; x < block.inputList.length; x++) {
      if (block.inputList[x].type == Blockly.INPUT_VALUE) {
        var childBlock = block.inputList[x].connection.targetBlock();
        if (childBlock) {
          var comment = Blockly.Generator.allNestedComments(childBlock);
          if (comment) {
            commentCode += Blockly.Generator.prefixLines(comment, '// ');
          }
        }
      }
    }
  }
  var nextBlock = block.nextConnection && block.nextConnection.targetBlock();
  var nextCode = this.blockToCode(nextBlock);
  return commentCode + code + nextCode;
};
<commit_msg>Fix JS's reserved words. (bug 89)<commit_after>/**
 * Visual Blocks Language
 *
 * Copyright 2012 Google Inc.
 * http://code.google.com/p/blockly/
 *
 * 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.
 */

/**
 * @fileoverview Helper functions for generating JavaScript for blocks.
 * @author fraser@google.com (Neil Fraser)
 */
'use strict';

Blockly.JavaScript = Blockly.Generator.get('JavaScript');

/**
 * List of illegal variable names.
 * This is not intended to be a security feature.  Blockly is 100% client-side,
 * so bypassing this list is trivial.  This is intended to prevent users from
 * accidentally clobbering a built-in object or function.
 * @private
 */
if (!Blockly.JavaScript.RESERVED_WORDS_) {
  Blockly.JavaScript.RESERVED_WORDS_ = '';
}

Blockly.JavaScript.RESERVED_WORDS_ +=
    'Blockly,' +  // In case JS is evaled in the current window.
    // https://developer.mozilla.org/en/JavaScript/Reference/Reserved_Words
    'break,case,catch,continue,debugger,default,delete,do,else,finally,for,function,if,in,instanceof,new,return,switch,this,throw,try,typeof,var,void,while,with,' +
    'class,enum,export,extends,import,super,implements,interface,let,package,private,protected,public,static,yield,' +
    'const,null,true,false,' +
    // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects
    'Array,ArrayBuffer,Boolean,Date,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,Error,eval,EvalError,Float32Array,Float64Array,Function,Infinity,Int16Array,Int32Array,Int8Array,isFinite,isNaN,Iterator,JSON,Math,NaN,Number,Object,parseFloat,parseInt,RangeError,ReferenceError,RegExp,StopIteration,String,SyntaxError,TypeError,Uint16Array,Uint32Array,Uint8Array,Uint8ClampedArray,undefined,uneval,URIError,' +
    // https://developer.mozilla.org/en/DOM/window
    'applicationCache,closed,Components,content,_content,controllers,crypto,defaultStatus,dialogArguments,directories,document,frameElement,frames,fullScreen,globalStorage,history,innerHeight,innerWidth,length,location,locationbar,localStorage,menubar,messageManager,mozAnimationStartTime,mozInnerScreenX,mozInnerScreenY,mozPaintCount,name,navigator,opener,outerHeight,outerWidth,pageXOffset,pageYOffset,parent,performance,personalbar,pkcs11,returnValue,screen,screenX,screenY,scrollbars,scrollMaxX,scrollMaxY,scrollX,scrollY,self,sessionStorage,sidebar,status,statusbar,toolbar,top,URL,window,' +
    'addEventListener,alert,atob,back,blur,btoa,captureEvents,clearImmediate,clearInterval,clearTimeout,close,confirm,disableExternalCapture,dispatchEvent,dump,enableExternalCapture,escape,find,focus,forward,GeckoActiveXObject,getAttention,getAttentionWithCycleCount,getComputedStyle,getSelection,home,matchMedia,maximize,minimize,moveBy,moveTo,mozRequestAnimationFrame,open,openDialog,postMessage,print,prompt,QueryInterface,releaseEvents,removeEventListener,resizeBy,resizeTo,restore,routeEvent,scroll,scrollBy,scrollByLines,scrollByPages,scrollTo,setCursor,setImmediate,setInterval,setResizable,setTimeout,showModalDialog,sizeToContent,stop,unescape,updateCommands,XPCNativeWrapper,XPCSafeJSObjectWrapper,' +
    'onabort,onbeforeunload,onblur,onchange,onclick,onclose,oncontextmenu,ondevicemotion,ondeviceorientation,ondragdrop,onerror,onfocus,onhashchange,onkeydown,onkeypress,onkeyup,onload,onmousedown,onmousemove,onmouseout,onmouseover,onmouseup,onmozbeforepaint,onpaint,onpopstate,onreset,onresize,onscroll,onselect,onsubmit,onunload,onpageshow,onpagehide,' +
    'Image,Option,Worker,' +
    // https://developer.mozilla.org/en/Gecko_DOM_Reference
    'Event,Range,File,FileReader,Blob,BlobBuilder,' +
    'Attr,CDATASection,CharacterData,Comment,console,DocumentFragment,DocumentType,DomConfiguration,DOMError,DOMErrorHandler,DOMException,DOMImplementation,DOMImplementationList,DOMImplementationRegistry,DOMImplementationSource,DOMLocator,DOMObject,DOMString,DOMStringList,DOMTimeStamp,DOMUserData,Entity,EntityReference,MediaQueryList,MediaQueryListListener,NameList,NamedNodeMap,Node,NodeFilter,NodeIterator,NodeList,Notation,Plugin,PluginArray,ProcessingInstruction,SharedWorker,Text,TimeRanges,Treewalker,TypeInfo,UserDataHandler,Worker,WorkerGlobalScope,' +
    'HTMLDocument,HTMLElement,HTMLAnchorElement,HTMLAppletElement,HTMLAudioElement,HTMLAreaElement,HTMLBaseElement,HTMLBaseFontElement,HTMLBodyElement,HTMLBRElement,HTMLButtonElement,HTMLCanvasElement,HTMLDirectoryElement,HTMLDivElement,HTMLDListElement,HTMLEmbedElement,HTMLFieldSetElement,HTMLFontElement,HTMLFormElement,HTMLFrameElement,HTMLFrameSetElement,HTMLHeadElement,HTMLHeadingElement,HTMLHtmlElement,HTMLHRElement,HTMLIFrameElement,HTMLImageElement,HTMLInputElement,HTMLKeygenElement,HTMLLabelElement,HTMLLIElement,HTMLLinkElement,HTMLMapElement,HTMLMenuElement,HTMLMetaElement,HTMLModElement,HTMLObjectElement,HTMLOListElement,HTMLOptGroupElement,HTMLOptionElement,HTMLOutputElement,HTMLParagraphElement,HTMLParamElement,HTMLPreElement,HTMLQuoteElement,HTMLScriptElement,HTMLSelectElement,HTMLSourceElement,HTMLSpanElement,HTMLStyleElement,HTMLTableElement,HTMLTableCaptionElement,HTMLTableCellElement,HTMLTableDataCellElement,HTMLTableHeaderCellElement,HTMLTableColElement,HTMLTableRowElement,HTMLTableSectionElement,HTMLTextAreaElement,HTMLTimeElement,HTMLTitleElement,HTMLTrackElement,HTMLUListElement,HTMLUnknownElement,HTMLVideoElement,' +
    'HTMLCanvasElement,CanvasRenderingContext2D,CanvasGradient,CanvasPattern,TextMetrics,ImageData,CanvasPixelArray,HTMLAudioElement,HTMLVideoElement,NotifyAudioAvailableEvent,HTMLCollection,HTMLAllCollection,HTMLFormControlsCollection,HTMLOptionsCollection,HTMLPropertiesCollection,DOMTokenList,DOMSettableTokenList,DOMStringMap,RadioNodeList,' +
    'SVGDocument,SVGElement,SVGAElement,SVGAltGlyphElement,SVGAltGlyphDefElement,SVGAltGlyphItemElement,SVGAnimationElement,SVGAnimateElement,SVGAnimateColorElement,SVGAnimateMotionElement,SVGAnimateTransformElement,SVGSetElement,SVGCircleElement,SVGClipPathElement,SVGColorProfileElement,SVGCursorElement,SVGDefsElement,SVGDescElement,SVGEllipseElement,SVGFilterElement,SVGFilterPrimitiveStandardAttributes,SVGFEBlendElement,SVGFEColorMatrixElement,SVGFEComponentTransferElement,SVGFECompositeElement,SVGFEConvolveMatrixElement,SVGFEDiffuseLightingElement,SVGFEDisplacementMapElement,SVGFEDistantLightElement,SVGFEFloodElement,SVGFEGaussianBlurElement,SVGFEImageElement,SVGFEMergeElement,SVGFEMergeNodeElement,SVGFEMorphologyElement,SVGFEOffsetElement,SVGFEPointLightElement,SVGFESpecularLightingElement,SVGFESpotLightElement,SVGFETileElement,SVGFETurbulenceElement,SVGComponentTransferFunctionElement,SVGFEFuncRElement,SVGFEFuncGElement,SVGFEFuncBElement,SVGFEFuncAElement,SVGFontElement,SVGFontFaceElement,SVGFontFaceFormatElement,SVGFontFaceNameElement,SVGFontFaceSrcElement,SVGFontFaceUriElement,SVGForeignObjectElement,SVGGElement,SVGGlyphElement,SVGGlyphRefElement,SVGGradientElement,SVGLinearGradientElement,SVGRadialGradientElement,SVGHKernElement,SVGImageElement,SVGLineElement,SVGMarkerElement,SVGMaskElement,SVGMetadataElement,SVGMissingGlyphElement,SVGMPathElement,SVGPathElement,SVGPatternElement,SVGPolylineElement,SVGPolygonElement,SVGRectElement,SVGScriptElement,SVGStopElement,SVGStyleElement,SVGSVGElement,SVGSwitchElement,SVGSymbolElement,SVGTextElement,SVGTextPathElement,SVGTitleElement,SVGTRefElement,SVGTSpanElement,SVGUseElement,SVGViewElement,SVGVKernElement,' +
    'SVGAngle,SVGColor,SVGICCColor,SVGElementInstance,SVGElementInstanceList,SVGLength,SVGLengthList,SVGMatrix,SVGNumber,SVGNumberList,SVGPaint,SVGPoint,SVGPointList,SVGPreserveAspectRatio,SVGRect,SVGStringList,SVGTransform,SVGTransformList,' +
    'SVGAnimatedAngle,SVGAnimatedBoolean,SVGAnimatedEnumeration,SVGAnimatedInteger,SVGAnimatedLength,SVGAnimatedLengthList,SVGAnimatedNumber,SVGAnimatedNumberList,SVGAnimatedPreserveAspectRatio,SVGAnimatedRect,SVGAnimatedString,SVGAnimatedTransformList,' +
    'SVGPathSegList,SVGPathSeg,SVGPathSegArcAbs,SVGPathSegArcRel,SVGPathSegClosePath,SVGPathSegCurvetoCubicAbs,SVGPathSegCurvetoCubicRel,SVGPathSegCurvetoCubicSmoothAbs,SVGPathSegCurvetoCubicSmoothRel,SVGPathSegCurvetoQuadraticAbs,SVGPathSegCurvetoQuadraticRel,SVGPathSegCurvetoQuadraticSmoothAbs,SVGPathSegCurvetoQuadraticSmoothRel,SVGPathSegLinetoAbs,SVGPathSegLinetoHorizontalAbs,SVGPathSegLinetoHorizontalRel,SVGPathSegLinetoRel,SVGPathSegLinetoVerticalAbs,SVGPathSegLinetoVerticalRel,SVGPathSegMovetoAbs,SVGPathSegMovetoRel,ElementTimeControl,TimeEvent,SVGAnimatedPathData,' +
    'SVGAnimatedPoints,SVGColorProfileRule,SVGCSSRule,SVGExternalResourcesRequired,SVGFitToViewBox,SVGLangSpace,SVGLocatable,SVGRenderingIntent,SVGStylable,SVGTests,SVGTextContentElement,SVGTextPositioningElement,SVGTransformable,SVGUnitTypes,SVGURIReference,SVGViewSpec,SVGZoomAndPan,';

/**
 * Order of operation ENUMs.
 * https://developer.mozilla.org/en/JavaScript/Reference/Operators/Operator_Precedence
 */
Blockly.JavaScript.ORDER_ATOMIC = 0;         // 0 "" ...
Blockly.JavaScript.ORDER_MEMBER = 1;         // . []
Blockly.JavaScript.ORDER_NEW = 1;            // new
Blockly.JavaScript.ORDER_FUNCTION_CALL = 2;  // ()
Blockly.JavaScript.ORDER_INCREMENT = 3;      // ++
Blockly.JavaScript.ORDER_DECREMENT = 3;      // --
Blockly.JavaScript.ORDER_LOGICAL_NOT = 4;    // !
Blockly.JavaScript.ORDER_BITWISE_NOT = 4;    // ~
Blockly.JavaScript.ORDER_UNARY_PLUS = 4;     // +
Blockly.JavaScript.ORDER_UNARY_NEGATION = 4; // -
Blockly.JavaScript.ORDER_TYPEOF = 4;         // typeof
Blockly.JavaScript.ORDER_VOID = 4;           // void
Blockly.JavaScript.ORDER_DELETE = 4;         // delete
Blockly.JavaScript.ORDER_MULTIPLICATION = 5; // *
Blockly.JavaScript.ORDER_DIVISION = 5;       // /
Blockly.JavaScript.ORDER_MODULUS = 5;        // %
Blockly.JavaScript.ORDER_ADDITION = 6;       // +
Blockly.JavaScript.ORDER_SUBTRACTION = 6;    // -
Blockly.JavaScript.ORDER_BITWISE_SHIFT = 7;  // << >> >>>
Blockly.JavaScript.ORDER_RELATIONAL = 8;     // < <= > >=
Blockly.JavaScript.ORDER_IN = 8;             // in
Blockly.JavaScript.ORDER_INSTANCEOF = 8;     // instanceof
Blockly.JavaScript.ORDER_EQUALITY = 9;       // == != === !==
Blockly.JavaScript.ORDER_BITWISE_AND = 10;   // &
Blockly.JavaScript.ORDER_BITWISE_XOR = 11;   // ^
Blockly.JavaScript.ORDER_BITWISE_OR = 12;    // |
Blockly.JavaScript.ORDER_LOGICAL_AND = 13;   // &&
Blockly.JavaScript.ORDER_LOGICAL_OR = 14;    // ||
Blockly.JavaScript.ORDER_CONDITIONAL = 15;   // ?:
Blockly.JavaScript.ORDER_ASSIGNMENT = 16;    // = += -= *= /= %= <<= >>= ...
Blockly.JavaScript.ORDER_COMMA = 17;         // ,
Blockly.JavaScript.ORDER_NONE = 99;          // (...)

/**
 * Initialise the database of variable names.
 */
Blockly.JavaScript.init = function() {
  // Create a dictionary of definitions to be printed before the code.
  Blockly.JavaScript.definitions_ = {};

  if (Blockly.Variables) {
    if (!Blockly.JavaScript.variableDB_) {
      Blockly.JavaScript.variableDB_ =
          new Blockly.Names(Blockly.JavaScript.RESERVED_WORDS_);
    } else {
      Blockly.JavaScript.variableDB_.reset();
    }

    var defvars = [];
    var variables = Blockly.Variables.allVariables();
    for (var x = 0; x < variables.length; x++) {
      defvars[x] = 'var ' +
          Blockly.JavaScript.variableDB_.getDistinctName(variables[x],
          Blockly.Variables.NAME_TYPE) + ';';
    }
    Blockly.JavaScript.definitions_['variables'] = defvars.join('\n');
  }
};

/**
 * Prepend the generated code with the variable definitions.
 * @param {string} code Generated code.
 * @return {string} Completed code.
 */
Blockly.JavaScript.finish = function(code) {
  // Convert the definitions dictionary into a list.
  var definitions = [];
  for (var name in Blockly.JavaScript.definitions_) {
    definitions.push(Blockly.JavaScript.definitions_[name]);
  }
  return definitions.join('\n\n') + '\n\n\n' + code;
};

/**
 * Naked values are top-level blocks with outputs that aren't plugged into
 * anything.  A trailing semicolon is needed to make this legal.
 * @param {string} line Line of generated code.
 * @return {string} Legal line of code.
 */
Blockly.JavaScript.scrubNakedValue = function(line) {
  return line + ';\n';
};

/**
 * Encode a string as a properly escaped JavaScript string, complete with
 * quotes.
 * @param {string} string Text to encode.
 * @return {string} JavaScript string.
 * @private
 */
Blockly.JavaScript.quote_ = function(string) {
  // TODO: This is a quick hack.  Replace with goog.string.quote
  string = string.replace(/\\/g, '\\\\')
                 .replace(/\n/g, '\\\n')
                 .replace(/'/g, '\\\'');
  return '\'' + string + '\'';
};

/**
 * Common tasks for generating JavaScript from blocks.
 * Handles comments for the specified block and any connected value blocks.
 * Calls any statements following this block.
 * @param {!Blockly.Block} block The current block.
 * @param {string} code The JavaScript code created for this block.
 * @return {string} JavaScript code with comments and subsequent blocks added.
 * @this {Blockly.CodeGenerator}
 * @private
 */
Blockly.JavaScript.scrub_ = function(block, code) {
  if (code === null) {
    // Block has handled code generation itself.
    return '';
  }
  var commentCode = '';
  // Only collect comments for blocks that aren't inline.
  if (!block.outputConnection || !block.outputConnection.targetConnection) {
    // Collect comment for this block.
    var comment = block.getCommentText();
    if (comment) {
      commentCode += Blockly.Generator.prefixLines(comment, '// ') + '\n';
    }
    // Collect comments for all value arguments.
    // Don't collect comments for nested statements.
    for (var x = 0; x < block.inputList.length; x++) {
      if (block.inputList[x].type == Blockly.INPUT_VALUE) {
        var childBlock = block.inputList[x].connection.targetBlock();
        if (childBlock) {
          var comment = Blockly.Generator.allNestedComments(childBlock);
          if (comment) {
            commentCode += Blockly.Generator.prefixLines(comment, '// ');
          }
        }
      }
    }
  }
  var nextBlock = block.nextConnection && block.nextConnection.targetBlock();
  var nextCode = this.blockToCode(nextBlock);
  return commentCode + code + nextCode;
};
<|endoftext|>


<commit_before>
	if( !window.$E ){var $E = {
		classHas: function(elem,className){var p = new RegExp('(^| )'+className+'( |$)');return (elem.className && elem.className.match(p));},
		classAdd: function(elem,className){if($E.classHas(elem,className)){return true;}elem.className += ' '+className;},
		classRemove: function(elem,className){var c = elem.className;var p = new RegExp('(^| )'+className+'( |$)');c = c.replace(p,' ').replace(/  /g,' ');elem.className = c;},
		classParentHas: function(elem,className,limit){
			limit = typeof limit !== 'undefined' ? limit : 1;
			if($E.classHas(elem,className)){return elem;}
			if(!elem.parentNode){return false;}
			do{if($E.classHas(elem.parentNode,className)){return elem.parentNode;}elem = elem.parentNode;}while(elem.parentNode && limit--);return false;
		},
		class: {
			exists: function(elem,className){var p = new RegExp('(^| )'+className+'( |$)');return (elem.className && elem.className.match(p));},
			add: function(elem,className){if($E.classHas(elem,className)){return true;}elem.className += ' '+className;},
			remove: function(elem,className){var c = elem.className;var p = new RegExp('(^| )'+className+'( |$)');c = c.replace(p,' ').replace(/  /g,' ');elem.className = c;}
		},
		parent: {
			find: function(elem,p){/* p = {tagName:false,className:false} */if(p.tagName){p.tagName = p.tagName.toUpperCase();}if(p.className){p.className = new RegExp('( |^)'+p.className+'( |$)');}while(elem.parentNode && ((p.tagName && elem.tagName!=p.tagName) || (p.className && !elem.className.match(p.className)))){elem = elem.parentNode;}if(!elem.parentNode){return false;}return $fix(elem);},
			match: function(elem,m){
				while( elem.parentNode && (!elem.matches || !elem.matches(m)) ){elem = elem.parentNode;}
				return ( elem.parentNode ) ? elem : false;
			}
		}
	}}

	if( !window['widgets'] ){window['widgets'] = {};}
	widgets.dropdown = function(elem){
		this.elem = elem;
		/* Evitamos que un mismo elemento se pueda instanciar 2 veces como dropdown */
		if( elem.getAttribute('data-dropdown') ){return false;}
		elem.setAttribute('data-dropdown',true);

		var ths = this;
		this.elem.addEventListener('click',function(e){ths.onClick.call(ths,e);});
		this.elem.addEventListener('touchstart',function(e){ths.onClick.call(ths,e);});
		this.elem.addEventListener('close',function(e){ths.close.call(ths,e);});
		/* Evitamos que se cierre al hacer click en el contenido */
		this.dropdown_menu = elem.querySelector('.dropdown-menu');
		if( this.dropdown_menu ){
			this.dropdown_menu.addEventListener('click',function(e){e.stopPropagation();});
			this.dropdown_menu.addEventListener('touchstart',function(e){e.stopPropagation();});
		}

		/* INI-botones de cerrar que incorpore el dropdown */
		var buttons = this.elem.querySelectorAll('.btn.close,.btn.btn-close');
		if(buttons.length){Array.prototype.slice.call(buttons).forEach(function(btn){
			btn.addEventListener('click',function(e){e.stopPropagation();ths.close.call(ths,e);});
			btn.addEventListener('touchstart',function(e){e.stopPropagation();ths.close.call(ths,e);});
		});}
		/* END-botones de cerrar que incorpore el dropdown */

		/* INI-selectBox */
		if( 0 && this.isSelectBox()){
			var dataParent = this.elem.getAttribute('data-value');
			this.setValue(dataParent,true);
			Array.prototype.slice.call(this.dropdown_menu.childNodes).forEach(function(it){
				if(!it.classList.contains('item')){return;}
				if(!dataParent){var val = it.getAttribute('data-value');ths.setValue.call(ths,val,true);dataParent = val;}
				it.addEventListener('click',function(e){e.stopPropagation();ths.setValue.call(ths,this.getAttribute('data-value'));ths.close.call(ths);});
			});
		}
		/* END-selectBox */

		/* INI-ajax*/
		if( 0 && this.isAjax()){
			var buttons = this.elem.querySelectorAll('.btn.ok,.btn.btn-ok');
			if(buttons.length){Array.prototype.slice.call(buttons).forEach(function(btn){
				btn.addEventListener('click',function(e){
					if(btn.tagName.toUpperCase() == 'BUTTON'){e.preventDefault();}/* Porque puede ser un button y nos envía el formulario */
					e.stopPropagation();
					ths.ajax.call(ths,e);
				});
			});}
		}
		/* END-ajax*/
	}
	widgets.dropdown.prototype.isDisabled = function(){return this.elem.classList.contains('disabled');};
	widgets.dropdown.prototype.isOpen = function(){return this.elem.classList.contains('active');};
	widgets.dropdown.prototype.isSelectBox = function(){return this.elem.classList.contains('select');};
	widgets.dropdown.prototype.isAjax = function(){return this.elem.classList.contains('ajax');};
	widgets.dropdown.prototype.isRemoveBox = function(){return this.elem.classList.contains('remove');};
	widgets.dropdown.prototype.mustSubmit = function(){return this.elem.classList.contains('autosubmit');};
	widgets.dropdown.prototype.setValue = function(dataToSet,initial){
		var parent = this.elem.getElementsByClassName('dropdown-menu');if(!parent.length){return;}parent = parent[0];
		Array.prototype.slice.call(parent.childNodes).forEach(function(it){
			if(!$E.class.exists(it,'item')){return;}
			var data = it.getAttribute('data-value');
			if(data != dataToSet){/* Buscamos el element coincidente, saltamos si este no fuera */return;}
			var ddw = $E.parent.find(it,{'className':'dropdown-toggle'});if(!ddw){return false;}
			var ipt = ddw.getElementsByClassName('input');if(ipt){
				Array.prototype.slice.call(ipt).forEach(function(y){y.value = data;});
			}
			var val = ddw.getElementsByClassName('value');if(val){
				Array.prototype.slice.call(val).forEach(function(y){y.innerHTML = it.innerHTML;});
			}
		});

		if(!initial && this.mustSubmit()){
			/* Widget calendar tiene su propio handler */
			if(this.elem.classList.contains('widget-calendar')){return false;}
			var form = $E.parent.find(this.elem,{'tagName':'form'});
			return form.submit();
		}
	};
	widgets.dropdown.prototype.onClick = function(e){
		var item = false;
		if(this.isDisabled()){return false;}
		e.preventDefault();
		e.stopPropagation();
		if(this.isOpen()){
			//VAR_dropdownToggled = false;
			if(this.isSelectBox() &&  (item = $E.parent.find(e.target,{'className':'item'})) ){
				var dataToSet = item.getAttribute('data-value');
				this.setValue(dataToSet);
			}
			return this.close();
		}
		return this.open();
	}
	widgets.dropdown.prototype.ajax = function(e){
		var ths  = this;
		var form = $E.parent.find(e.target,{'tagName':'form'});if(!form){return false;}
		var ddm  = this.elem.querySelector('.dropdown-menu');if(!ddm){return false;}
		var info = this.elem.querySelector('div[data-state="info"]');

		ddm.classList.remove('state-main','state-working','state-end');
		ddm.classList.add('state-working');

		var params = $toUrl($parseForm(form));
		ajaxPetition(window.location.href,params,function(ajax){
			var r = $json.decode(ajax.responseText);
			if( 'errorDescription' in r ){alert(print_r(r));return;}
			ddm.classList.remove('state-main','state-working','state-end');

			if( 'html' in r && info ){
				info.innerHTML = r.html;
				ddm.classList.add('state-info');
			}
		});

		return false;
	};
	widgets.dropdown.prototype.open = function(){
		//FIXME: evento before open
		var btn = $E.parent.find(this.elem,{'className':'dropdown-toggle'});
		var cbOnBeforeOpen = btn.getAttribute('data-dropdown-onBeforeOpen');

		/* INI-Soporte para recaptcha */
		if( window.grecaptcha && (elems = this.elem.querySelectorAll('.g-recaptcha')) ){
			var key  = false;
			var elem = false;
			Array.prototype.slice.call(elems).forEach(function(elem){
				if( elem.firstChild ){return;}
				key = elem.getAttribute('data-sitekey');
				if(key){grecaptcha.render(elem,{'sitekey':key,'theme':'light'});}
			});
		}
		/* END-Soporte para recaptcha */
		/* INI-Search for updatable elements */
		if( (this.tmp = this.elem.querySelectorAll('.widget-update')) ){
			/* Give some time to calculate bounding correctly */
			setTimeout(function(){
				Array.prototype.slice.call(this.tmp).forEach(function(elem){
					var event = new CustomEvent('widget-should-update',{'detail':{},'bubbles':true,'cancelable':true});
					elem.dispatchEvent(event);
				});
			}.bind(this),2);
		}
		/* END-Search for updatable elements */

		btn.classList.toggle('active');
		if( cbOnBeforeOpen && this.isOpen() ){$execByString(cbOnBeforeOpen,[false,btn]);}

		/* INI-Widget-list */
		if( (elems = this.elem.querySelectorAll('.widget-list')) ){
			Array.prototype.slice.call(elems).forEach(function(elem){
				new widgets.list(elem);
			});
		}
		/* END-Widget-list */

		if( !window.VAR ){window.VAR = {};}
		window.VAR.dropdown = this.elem;
		var bodyWidth = (document.body.offsetWidth);
		var pos = this.dropdown_menu.getBoundingClientRect();
		if( pos.width > (bodyWidth-20) ){
			this.dropdown_menu.style.width = (bodyWidth-20)+'px';
			pos  = this.dropdown_menu.getBoundingClientRect();
		}
		var rpos = bodyWidth-(pos.left+pos.width);
		/* If the infoBox is out the page, fix it to the right border */
		if( rpos < 10 ){this.dropdown_menu.style.left = this.dropdown_menu.offsetLeft+rpos-10+'px';}

		this.dropdown_menu.classList.remove('state-end');
		//FIXME: evento open
	}
	widgets.dropdown.prototype.close = function(){
		var btn = $E.parent.find(this.elem,{'className':'dropdown-toggle'});
		if(btn){btn.classList.remove('active');}
	};
	/* END-dropdown */

	addEventListener('DOMContentLoaded',function(){
		drodropdown_init();
	});
	if (document.readyState === 'complete' || document.readyState === 'loaded' || document.readyState === 'interactive') {
		drodropdown_init();
	}

	function drodropdown_init(){
		/* INI-dropdown */
		if( !window.VAR ){window.VAR = {};}
		window.VAR.dropdown = false;
		var dropdownToggles = document.querySelectorAll('.dropdown-toggle');
		Array.prototype.slice.call(dropdownToggles).forEach(function(v){var d = new widgets.dropdown(v);});
		var body = document.body;
		body.addEventListener('click',function(event){
			if( window.VAR.dropdown ){
				var event = new CustomEvent('close',{'detail':{},'bubbles':true,'cancelable':true});
				window.VAR.dropdown.dispatchEvent(event);
			}
		});
		/* END-dropdown */
	}

<commit_msg>Update widget.dropdown.js<commit_after>
	if( !window.$E ){var $E = {
		classHas: function(elem,className){var p = new RegExp('(^| )'+className+'( |$)');return (elem.className && elem.className.match(p));},
		classAdd: function(elem,className){if($E.classHas(elem,className)){return true;}elem.className += ' '+className;},
		classRemove: function(elem,className){var c = elem.className;var p = new RegExp('(^| )'+className+'( |$)');c = c.replace(p,' ').replace(/  /g,' ');elem.className = c;},
		classParentHas: function(elem,className,limit){
			limit = typeof limit !== 'undefined' ? limit : 1;
			if($E.classHas(elem,className)){return elem;}
			if(!elem.parentNode){return false;}
			do{if($E.classHas(elem.parentNode,className)){return elem.parentNode;}elem = elem.parentNode;}while(elem.parentNode && limit--);return false;
		},
		class: {
			exists: function(elem,className){var p = new RegExp('(^| )'+className+'( |$)');return (elem.className && elem.className.match(p));},
			add: function(elem,className){if($E.classHas(elem,className)){return true;}elem.className += ' '+className;},
			remove: function(elem,className){var c = elem.className;var p = new RegExp('(^| )'+className+'( |$)');c = c.replace(p,' ').replace(/  /g,' ');elem.className = c;}
		},
		parent: {
			find: function(elem,p){/* p = {tagName:false,className:false} */if(p.tagName){p.tagName = p.tagName.toUpperCase();}if(p.className){p.className = new RegExp('( |^)'+p.className+'( |$)');}while(elem.parentNode && ((p.tagName && elem.tagName!=p.tagName) || (p.className && !elem.className.match(p.className)))){elem = elem.parentNode;}if(!elem.parentNode){return false;}return elem;},
			match: function(elem,m){
				while( elem.parentNode && (!elem.matches || !elem.matches(m)) ){elem = elem.parentNode;}
				return ( elem.parentNode ) ? elem : false;
			}
		}
	}}

	if( !window['widgets'] ){window['widgets'] = {};}
	widgets.dropdown = function(elem){
		this.elem = elem;
		/* Evitamos que un mismo elemento se pueda instanciar 2 veces como dropdown */
		if( elem.getAttribute('data-dropdown') ){return false;}
		elem.setAttribute('data-dropdown',true);

		var ths = this;
		this.elem.addEventListener('click',function(e){ths.onClick.call(ths,e);});
		this.elem.addEventListener('touchstart',function(e){ths.onClick.call(ths,e);});
		this.elem.addEventListener('close',function(e){ths.close.call(ths,e);});
		/* Evitamos que se cierre al hacer click en el contenido */
		this.dropdown_menu = elem.querySelector('.dropdown-menu');
		if( this.dropdown_menu ){
			this.dropdown_menu.addEventListener('click',function(e){e.stopPropagation();});
			this.dropdown_menu.addEventListener('touchstart',function(e){e.stopPropagation();});
		}

		/* INI-botones de cerrar que incorpore el dropdown */
		var buttons = this.elem.querySelectorAll('.btn.close,.btn.btn-close');
		if(buttons.length){Array.prototype.slice.call(buttons).forEach(function(btn){
			btn.addEventListener('click',function(e){e.stopPropagation();ths.close.call(ths,e);});
			btn.addEventListener('touchstart',function(e){e.stopPropagation();ths.close.call(ths,e);});
		});}
		/* END-botones de cerrar que incorpore el dropdown */

		/* INI-selectBox */
		if( 0 && this.isSelectBox()){
			var dataParent = this.elem.getAttribute('data-value');
			this.setValue(dataParent,true);
			Array.prototype.slice.call(this.dropdown_menu.childNodes).forEach(function(it){
				if(!it.classList.contains('item')){return;}
				if(!dataParent){var val = it.getAttribute('data-value');ths.setValue.call(ths,val,true);dataParent = val;}
				it.addEventListener('click',function(e){e.stopPropagation();ths.setValue.call(ths,this.getAttribute('data-value'));ths.close.call(ths);});
			});
		}
		/* END-selectBox */

		/* INI-ajax*/
		if( 0 && this.isAjax()){
			var buttons = this.elem.querySelectorAll('.btn.ok,.btn.btn-ok');
			if(buttons.length){Array.prototype.slice.call(buttons).forEach(function(btn){
				btn.addEventListener('click',function(e){
					if(btn.tagName.toUpperCase() == 'BUTTON'){e.preventDefault();}/* Porque puede ser un button y nos envía el formulario */
					e.stopPropagation();
					ths.ajax.call(ths,e);
				});
			});}
		}
		/* END-ajax*/
	}
	widgets.dropdown.prototype.isDisabled = function(){return this.elem.classList.contains('disabled');};
	widgets.dropdown.prototype.isOpen = function(){return this.elem.classList.contains('active');};
	widgets.dropdown.prototype.isSelectBox = function(){return this.elem.classList.contains('select');};
	widgets.dropdown.prototype.isAjax = function(){return this.elem.classList.contains('ajax');};
	widgets.dropdown.prototype.isRemoveBox = function(){return this.elem.classList.contains('remove');};
	widgets.dropdown.prototype.mustSubmit = function(){return this.elem.classList.contains('autosubmit');};
	widgets.dropdown.prototype.setValue = function(dataToSet,initial){
		var parent = this.elem.getElementsByClassName('dropdown-menu');if(!parent.length){return;}parent = parent[0];
		Array.prototype.slice.call(parent.childNodes).forEach(function(it){
			if(!$E.class.exists(it,'item')){return;}
			var data = it.getAttribute('data-value');
			if(data != dataToSet){/* Buscamos el element coincidente, saltamos si este no fuera */return;}
			var ddw = $E.parent.find(it,{'className':'dropdown-toggle'});if(!ddw){return false;}
			var ipt = ddw.getElementsByClassName('input');if(ipt){
				Array.prototype.slice.call(ipt).forEach(function(y){y.value = data;});
			}
			var val = ddw.getElementsByClassName('value');if(val){
				Array.prototype.slice.call(val).forEach(function(y){y.innerHTML = it.innerHTML;});
			}
		});

		if(!initial && this.mustSubmit()){
			/* Widget calendar tiene su propio handler */
			if(this.elem.classList.contains('widget-calendar')){return false;}
			var form = $E.parent.find(this.elem,{'tagName':'form'});
			return form.submit();
		}
	};
	widgets.dropdown.prototype.onClick = function(e){
		var item = false;
		if(this.isDisabled()){return false;}
		e.preventDefault();
		e.stopPropagation();
		if(this.isOpen()){
			//VAR_dropdownToggled = false;
			if(this.isSelectBox() &&  (item = $E.parent.find(e.target,{'className':'item'})) ){
				var dataToSet = item.getAttribute('data-value');
				this.setValue(dataToSet);
			}
			return this.close();
		}
		return this.open();
	}
	widgets.dropdown.prototype.ajax = function(e){
		var ths  = this;
		var form = $E.parent.find(e.target,{'tagName':'form'});if(!form){return false;}
		var ddm  = this.elem.querySelector('.dropdown-menu');if(!ddm){return false;}
		var info = this.elem.querySelector('div[data-state="info"]');

		ddm.classList.remove('state-main','state-working','state-end');
		ddm.classList.add('state-working');

		var params = $toUrl($parseForm(form));
		ajaxPetition(window.location.href,params,function(ajax){
			var r = $json.decode(ajax.responseText);
			if( 'errorDescription' in r ){alert(print_r(r));return;}
			ddm.classList.remove('state-main','state-working','state-end');

			if( 'html' in r && info ){
				info.innerHTML = r.html;
				ddm.classList.add('state-info');
			}
		});

		return false;
	};
	widgets.dropdown.prototype.open = function(){
		//FIXME: evento before open
		var btn = $E.parent.find(this.elem,{'className':'dropdown-toggle'});
		var cbOnBeforeOpen = btn.getAttribute('data-dropdown-onBeforeOpen');

		/* INI-Soporte para recaptcha */
		if( window.grecaptcha && (elems = this.elem.querySelectorAll('.g-recaptcha')) ){
			var key  = false;
			var elem = false;
			Array.prototype.slice.call(elems).forEach(function(elem){
				if( elem.firstChild ){return;}
				key = elem.getAttribute('data-sitekey');
				if(key){grecaptcha.render(elem,{'sitekey':key,'theme':'light'});}
			});
		}
		/* END-Soporte para recaptcha */
		/* INI-Search for updatable elements */
		if( (this.tmp = this.elem.querySelectorAll('.widget-update')) ){
			/* Give some time to calculate bounding correctly */
			setTimeout(function(){
				Array.prototype.slice.call(this.tmp).forEach(function(elem){
					var event = new CustomEvent('widget-should-update',{'detail':{},'bubbles':true,'cancelable':true});
					elem.dispatchEvent(event);
				});
			}.bind(this),2);
		}
		/* END-Search for updatable elements */

		btn.classList.toggle('active');
		if( cbOnBeforeOpen && this.isOpen() ){$execByString(cbOnBeforeOpen,[false,btn]);}

		/* INI-Widget-list */
		if( (elems = this.elem.querySelectorAll('.widget-list')) ){
			Array.prototype.slice.call(elems).forEach(function(elem){
				new widgets.list(elem);
			});
		}
		/* END-Widget-list */

		if( !window.VAR ){window.VAR = {};}
		window.VAR.dropdown = this.elem;
		var bodyWidth = (document.body.offsetWidth);
		var pos = this.dropdown_menu.getBoundingClientRect();
		if( pos.width > (bodyWidth-20) ){
			this.dropdown_menu.style.width = (bodyWidth-20)+'px';
			pos  = this.dropdown_menu.getBoundingClientRect();
		}
		var rpos = bodyWidth-(pos.left+pos.width);
		/* If the infoBox is out the page, fix it to the right border */
		if( rpos < 10 ){this.dropdown_menu.style.left = this.dropdown_menu.offsetLeft+rpos-10+'px';}

		this.dropdown_menu.classList.remove('state-end');
		//FIXME: evento open
	}
	widgets.dropdown.prototype.close = function(){
		var btn = $E.parent.find(this.elem,{'className':'dropdown-toggle'});
		if(btn){btn.classList.remove('active');}
	};
	/* END-dropdown */

	addEventListener('DOMContentLoaded',function(){
		drodropdown_init();
	});
	if (document.readyState === 'complete' || document.readyState === 'loaded' || document.readyState === 'interactive') {
		drodropdown_init();
	}

	function drodropdown_init(){
		/* INI-dropdown */
		if( !window.VAR ){window.VAR = {};}
		window.VAR.dropdown = false;
		var dropdownToggles = document.querySelectorAll('.dropdown-toggle');
		Array.prototype.slice.call(dropdownToggles).forEach(function(v){var d = new widgets.dropdown(v);});
		var body = document.body;
		body.addEventListener('click',function(event){
			if( window.VAR.dropdown ){
				var event = new CustomEvent('close',{'detail':{},'bubbles':true,'cancelable':true});
				window.VAR.dropdown.dispatchEvent(event);
			}
		});
		/* END-dropdown */
	}

<|endoftext|>


<commit_before>/**
 * Created by tusharmathur on 5/15/15.
 */
'use strict'
const download = require('./download').download
const create = require('./create').create
const ob = require('./observables')

class Download {
  constructor (options) {
    this.options = options
    this.options.mtdPath = this.options.path + '.mtd'
  }

  start () {
    return create(ob, this.options)
      .toPromise()
      .then(() => download(ob, this.options).toPromise())
  }

  stop () {
  }
}
module.exports = Download
<commit_msg>refactor(mtd): use toPromise at end<commit_after>/**
 * Created by tusharmathur on 5/15/15.
 */
'use strict'
const download = require('./download').download
const create = require('./create').create
const ob = require('./observables')

class Download {
  constructor (options) {
    this.options = options
    this.options.mtdPath = this.options.path + '.mtd'
  }

  start () {
    return create(ob, this.options)
      .flatMap(() => download(ob, this.options))
      .toPromise()
  }

  stop () {
  }
}
module.exports = Download
<|endoftext|>


<commit_before>var featureAttributeListLabels = {
    'noLayer': OpenLayers.i18n('No layer selected'),
    'noAttribute': OpenLayers.i18n('No attributes selected'),
}

gbi.widgets = gbi.widgets || {};

gbi.widgets.FeatureAttributeList = function(thematicalVector, options) {
    if(!(thematicalVector instanceof gbi.widgets.ThematicalVector)) {
        return;
    }
    var self = this;
    var defaults = {
        element: 'featureattributelist',
        mode: 'exact'
    }
    this.options = $.extend({}, defaults, options);
    this.element = $('#' + this.options.element);
    this.thematicalVector = thematicalVector;
    this.editor = thematicalVector.editor;
    this.activeLayer = this.editor.layerManager.active();
    if(this.options.featurePopup == 'hover') {
        this.hoverCtrl = new gbi.Controls.Hover(this.activeLayer);
        this.editor.map.addControl(this.hoverCtrl);
    }
    $(gbi).on('gbi.layermanager.layer.active', function(event, layer) {
        self.activeLayer = layer;
        self.hoverCtrl.changeLayer(layer);
        if(self.activeLayer) {
            self._registerLayerEvents(self.activeLayer);
            self.attributes = self.activeLayer.featuresAttributes() || [];
        } else {
            self.attributes = [];
        }
        self.render();

    });
    if(this.activeLayer) {
        this._registerLayerEvents(this.activeLayer);
        this.attributes = this.activeLayer.featuresAttributes() || [];
    } else {
        this.attributes = [];
    }
    self.render();
};
gbi.widgets.FeatureAttributeList.prototype = {
    render: function() {
        var self = this;
        this.element.empty();

        if(!self.activeLayer) {
            this.element.append($('
' + featureAttributeListLabels.noLayer + '
'));
            return;
        }

        var attributes = self.activeLayer ? self.activeLayer.listAttributes() || [] : [];

        if(attributes.length == 0) {
            this.element.append($('
' + featureAttributeListLabels.noAttribute + '
'));
            return;
        }

        this.element.append(tmpl(
            gbi.widgets.FeatureAttributeList.template, {
                attributes: self.activeLayer.listAttributes() || self.attributes,
                features: self.activeLayer.features
            }
        ));

        this.element.find('.show-feature').click(function() {
            var element = $(this);
            var feature = self.activeLayer.features[element.attr('id')];
            self.activeLayer.showFeature(feature);
        });
    },
    _registerLayerEvents: function(layer) {
        var self = this;
        if(layer instanceof gbi.Layers.SaveableVector && !layer.loaded) {
            $(layer).on('gbi.layer.couch.loadFeaturesEnd', function() {
                self.attributes = layer.featuresAttributes();
                self.render();
            });
        }
        $(layer).on('gbi.layer.vector.listAttributesChanged', function() {
            self.render();
        });
        $(layer).on('gbi.layer.vector.featureAttributeChanged', function() {
            self.render();
        });
    }
};

gbi.widgets.FeatureAttributeList.template = '\

\

\
            <% for(var key in attributes) { %>\
                <%=attributes[key]%>	\
            <% } %>\

\
        <% for(var f_key in features) { %>\
            <tr class="show-feature" id="<%=f_key%>">\
                <% for(var a_key in attributes) { %>\
                    \
                    <% if(features[f_key].attributes[attributes[a_key]]) { %>\
                        <%=features[f_key].attributes[attributes[a_key]]%>\
                    <% } else {%>\
                         \
                    <% } %>\
                    	\
                <% } %>\

\
        <% } %>\

\
';<commit_msg>Allow to render specified list of features<commit_after>var featureAttributeListLabels = {
    'noLayer': OpenLayers.i18n('No layer selected'),
    'noAttribute': OpenLayers.i18n('No attributes selected'),
    'reset': OpenLayers.i18n('Show all features')
}

gbi.widgets = gbi.widgets || {};

gbi.widgets.FeatureAttributeList = function(thematicalVector, options) {
    if(!(thematicalVector instanceof gbi.widgets.ThematicalVector)) {
        return;
    }
    var self = this;
    var defaults = {
        element: 'featureattributelist',
        mode: 'exact'
    }
    this.options = $.extend({}, defaults, options);
    this.element = $('#' + this.options.element);
    this.thematicalVector = thematicalVector;
    this.editor = thematicalVector.editor;
    this.activeLayer = this.editor.layerManager.active();
    if(this.options.featurePopup == 'hover') {
        this.hoverCtrl = new gbi.Controls.Hover(this.activeLayer);
        this.editor.map.addControl(this.hoverCtrl);
    }
    $(gbi).on('gbi.layermanager.layer.active', function(event, layer) {
        self.activeLayer = layer;
        self.hoverCtrl.changeLayer(layer);
        if(self.activeLayer) {
            self._registerLayerEvents(self.activeLayer);
            self.attributes = self.activeLayer.featuresAttributes() || [];
        } else {
            self.attributes = [];
        }
        self.render();

    });
    if(this.activeLayer) {
        this._registerLayerEvents(this.activeLayer);
        this.attributes = this.activeLayer.featuresAttributes() || [];
    } else {
        this.attributes = [];
    }
    self.render();
};
gbi.widgets.FeatureAttributeList.prototype = {
    render: function(features) {
        var self = this;
        this.element.empty();

        if(!self.activeLayer) {
            this.element.append($('
' + featureAttributeListLabels.noLayer + '
'));
            return;
        }
        var attributes = self.activeLayer ? self.activeLayer.listAttributes() || [] : [];

        if(attributes.length == 0) {
            this.element.append($('
' + featureAttributeListLabels.noAttribute + '
'));
            return;
        }

        this.element.append(tmpl(
            gbi.widgets.FeatureAttributeList.template, {
                attributes: this.options.fullList ? self.attributes : self.activeLayer.listAttributes() || self.attributes,
                features: features || self.activeLayer.features,
                resetButton: features ? true : false
            }
        ));

        if(features) {
            $('#reset-list').click(function() {
                self.render();
            });
        }

        this.element.find('.show-feature').click(function() {
            var element = $(this);
            var feature = self.activeLayer.features[element.attr('id')];
            self.activeLayer.showFeature(feature);
        });
    },
    showFilteredFeatures: function(value) {
        var filteredFeatures = this.activeLayer.filteredFeatures().result;
        var features;
        $.each(filteredFeatures, function(idx, filterItem) {
            if(filterItem.value == value) {
                features = filterItem.features;
                return true;
            }
        });
        this.render(features);
    },
    _registerLayerEvents: function(layer) {
        var self = this;
        if(layer instanceof gbi.Layers.SaveableVector && !layer.loaded) {
            $(layer).on('gbi.layer.couch.loadFeaturesEnd', function() {
                self.attributes = layer.featuresAttributes();
                self.render();
            });
        }
        $(layer).on('gbi.layer.vector.listAttributesChanged', function() {
            self.render();
        });
        $(layer).on('gbi.layer.vector.featureAttributeChanged', function() {
            self.render();
        });
    }
};

gbi.widgets.FeatureAttributeList.template = '\
    <% if(resetButton) { %>\
        <button class="btn btn-small" id="reset-list">' + featureAttributeListLabels.reset + '</button>\
    <% } %>\

\

\
            <% for(var key in attributes) { %>\
                <%=attributes[key]%>	\
            <% } %>\
             	\

\
        <% for(var f_key in features) { %>\

\
                <% for(var a_key in attributes) { %>\
                    \
                    <% if(features[f_key].attributes[attributes[a_key]]) { %>\
                        <%=features[f_key].attributes[attributes[a_key]]%>\
                    <% } else {%>\
                         \
                    <% } %>\
                    	\
                <% } %>\
                \
                    <button class="btn btn-small show-feature" id="<%=f_key%>">\
                        \
                    </button>\
                	\

\
        <% } %>\

\
';
<|endoftext|>


<commit_before>/*
 * This file exists solely for nasty browser hacks, fallbacks and fixes.
 *
 * It is envisaged that we DELETE the code-snippets as soon as support for each targeted browser is deprecated. Therefore watch out for the «TARGETED BROWSER»-comments at the beginning of each function in this file.
 *
 */

export function init() {
    objectFitForIE();

    replaceSVGLogoForIE();

    addFixedforIEClass();
}

function copyPropertiesFromOldImage(oldImg, fakeImg, objectFitVal) {
    let imageSource = oldImg.src,
        imageClasses = oldImg.className;

    oldImg.style.display = 'none';

    if (imageSource.indexOf('Placeholder-16to9.svg') < 0) {
        fakeImg.style.backgroundSize = objectFitVal;
    }
    fakeImg.style.backgroundImage = 'url(' + imageSource + ')';
    fakeImg.style.backgroundPosition = 'center center';
    fakeImg.style.backgroundRepeat = 'no-repeat';
    fakeImg.className = imageClasses + ' js-fake-image-object-fit';

}
function getObjectFitValue(elem) {
    let objectFitVal = 'contain';

    if (  elem.currentStyle ) {
        objectFitVal = elem.currentStyle.getAttribute('object-fit');
    }

    return objectFitVal;

}

export function polyfillObjectFit(element) {

    //only do something if there is no objectFit
    if ('objectFit' in document.documentElement.style ) {
        return;
    }

    // only do something if there's an image.
    if (!element) {
        return;
    }
    //only do something if the image has a src
    if (!element.getAttribute('src')) {
        return;
    }

    let objectFitVal = getObjectFitValue(element);

    if (objectFitVal === 'contain' || objectFitVal === 'cover') {

        let fakeImg = document.createElement('div');

        element.parentNode.insertBefore(fakeImg, element.parentNode.childNodes[0]);

        element.addEventListener('load', event => {
            let oldImg = event.currentTarget,
                fakeImg = oldImg.parentElement.getElementsByClassName('js-fake-image-object-fit')[0],
                objectFitVal = getObjectFitValue(oldImg);

            copyPropertiesFromOldImage(oldImg, fakeImg, objectFitVal);
        });

        copyPropertiesFromOldImage(element, fakeImg, objectFitVal);

    }

}
function objectFitForIE() {

    /*
    * TARGETED BROWSER: IE 11 and Edge <= 15
    *
    * This one is a object-fit-Fallback for Browsers not supporting object-fit --> IE 11 and Edge <= 15.
    *
    * It checks what kind of object-fit is used on the img ('contain' or 'cover') and sets the appropriate alternative
    * BackgroundSize. Except in Edge, where we set the 'contain'-value per default (because we use 'contain' more often
    * than 'cover' in the RA). We do this, because Edge does not let us read out the value of the prop it does not
    * understand (object-fit) - unlike good-old IE.
    * Additionally, a load-event-listener is bound to the image. If the source is changed (e.g. in image galleries,
    * where images after the 2nd are only loaded on interaction with the gallery), we do the same procedure again.
    */

    if ('objectFit' in document.documentElement.style === false) {

        const relevantClasses = [
                '.article-media--image',
                '.article-teaser__wrapper',
                '.carousel__item',
                '.poll-media--image',
                '.listing-item__media',
                '.media-still__image',
                '.teaser:not(.teaser--with-medium) .teaser__medium-wrapper', // teaser--with-medium do also contain a media-still__image!
                '.teaser__longform-medium',
                '.meteo-anchorman-image'
            ],
            containers = document.querySelectorAll( relevantClasses.join(', ') );

        for (let i = 0; i < containers.length; i++) {
            polyfillObjectFit(containers[i].querySelector('img'));
        }
    }
}

/*
 * TARGETED BROWSER: IE 11
 *
 * This one adds the value of a data-attribute named "ie-fix" to the class-list of this element – if the user agent is IE11.
 */

function addFixedforIEClass() {
    if (navigator.userAgent.indexOf('MSIE')!==-1 || navigator.appVersion.indexOf('Trident/') > 0) {
        const dataAttr = 'data-iefix';
        let elementsToFix = document.querySelectorAll('['+dataAttr+']');

        for(let i = 0; i < elementsToFix.length; ++i) {
            let ieFixClassName = elementsToFix[i].getAttribute(dataAttr);
            elementsToFix[i].classList.add(ieFixClassName);
        }
    }
}

/*
 * TARGETED BROWSER: IE 11
 *
 * IE11 _sometimes_ has problems with scaled images with svg sources. The usual remedies (viewbox, no widths, etc.)
 * didn't help. This is the last resort - hiding the img and setting a background-image on the parent.
 */
function replaceSVGLogoForIE() {
    if (navigator.userAgent.indexOf('MSIE')!==-1 || navigator.appVersion.indexOf('Trident/') > 0) {
        let bu = document.body.getAttribute('data-bu');
        let fixClassNames = (bu === 'rtr') ? 'header-startlink--fixed-for-ie header-startlink--fixed-for-ie-rtr' : 'header-startlink--fixed-for-ie';

        if(document.getElementsByClassName('header-startlink').length > 0) {
            document.getElementsByClassName('header-startlink')[0].classList.add(fixClassNames);
        }
    }
}

/*
 * TARGETED BROWSER: IE 11
 *
 * polyfill for findIndex.
 * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex
 */
if (!Array.prototype.findIndex) {
    Object.defineProperty(Array.prototype, 'findIndex', {
        value: function(predicate) {
            // 1. Let O be ? ToObject(this value).
            if (this == null) {
                throw new TypeError('"this" is null or not defined');
            }

            let o = Object(this);

            // 2. Let len be ? ToLength(? Get(O, "length")).
            let len = o.length >>> 0;

            // 3. If IsCallable(predicate) is false, throw a TypeError exception.
            if (typeof predicate !== 'function') {
                throw new TypeError('predicate must be a function');
            }

            // 4. If thisArg was supplied, let T be thisArg; else let T be undefined.
            let thisArg = arguments[1];

            // 5. Let k be 0.
            let k = 0;

            // 6. Repeat, while k < len
            while (k < len) {
                // a. Let Pk be ! ToString(k).
                // b. Let kValue be ? Get(O, Pk).
                // c. Let testResult be ToBoolean(? Call(predicate, T, « kValue, k, O »)).
                // d. If testResult is true, return k.
                let kValue = o[k];
                if (predicate.call(thisArg, kValue, k, o)) {
                    return k;
                }
                // e. Increase k by 1.
                k++;
            }

            // 7. Return -1.
            return -1;
        },
        configurable: true,
        writable: true
    });
}
<commit_msg>add onairflyout to object-fit images<commit_after>/*
 * This file exists solely for nasty browser hacks, fallbacks and fixes.
 *
 * It is envisaged that we DELETE the code-snippets as soon as support for each targeted browser is deprecated. Therefore watch out for the «TARGETED BROWSER»-comments at the beginning of each function in this file.
 *
 */

export function init() {
    objectFitForIE();

    replaceSVGLogoForIE();

    addFixedforIEClass();
}

function copyPropertiesFromOldImage(oldImg, fakeImg, objectFitVal) {
    let imageSource = oldImg.src,
        imageClasses = oldImg.className;

    oldImg.style.display = 'none';

    if (imageSource.indexOf('Placeholder-16to9.svg') < 0) {
        fakeImg.style.backgroundSize = objectFitVal;
    }
    fakeImg.style.backgroundImage = 'url(' + imageSource + ')';
    fakeImg.style.backgroundPosition = 'center center';
    fakeImg.style.backgroundRepeat = 'no-repeat';
    fakeImg.className = imageClasses + ' js-fake-image-object-fit';
}

function getObjectFitValue(elem) {
    let objectFitVal = 'contain';

    if (  elem.currentStyle ) {
        objectFitVal = elem.currentStyle.getAttribute('object-fit');
    }

    return objectFitVal;
}

export function polyfillObjectFit(element) {
    //only do something if there is no objectFit
    if ('objectFit' in document.documentElement.style ) {
        return;
    }

    // only do something if there's an image.
    if (!element) {
        return;
    }
    //only do something if the image has a src
    if (!element.getAttribute('src')) {
        return;
    }

    let objectFitVal = getObjectFitValue(element);

    if (objectFitVal === 'contain' || objectFitVal === 'cover') {
        let fakeImg = document.createElement('div');

        element.parentNode.insertBefore(fakeImg, element.parentNode.childNodes[0]);

        element.addEventListener('load', event => {
            let oldImg = event.currentTarget,
                fakeImg = oldImg.parentElement.getElementsByClassName('js-fake-image-object-fit')[0],
                objectFitVal = getObjectFitValue(oldImg);

            copyPropertiesFromOldImage(oldImg, fakeImg, objectFitVal);
        });

        copyPropertiesFromOldImage(element, fakeImg, objectFitVal);
    }
}

function objectFitForIE() {
    /**
     * TARGETED BROWSER: IE 11 and Edge <= 15
     *
     * This one is a object-fit-Fallback for Browsers not supporting object-fit --> IE 11 and Edge <= 15.
     *
     * It checks what kind of object-fit is used on the img ('contain' or 'cover') and sets the appropriate alternative
     * BackgroundSize. Except in Edge, where we set the 'contain'-value per default (because we use 'contain' more often
     * than 'cover' in the RA). We do this, because Edge does not let us read out the value of the prop it does not
     * understand (object-fit) - unlike good-old IE.
     * Additionally, a load-event-listener is bound to the image. If the source is changed (e.g. in image galleries,
     * where images after the 2nd are only loaded on interaction with the gallery), we do the same procedure again.
     */

    if ('objectFit' in document.documentElement.style === false) {

        const relevantClasses = [
                '.article-media--image',
                '.article-teaser__wrapper',
                '.carousel__item',
                '.poll-media--image',
                '.listing-item__media',
                '.media-still__image',
                '.teaser:not(.teaser--with-medium) .teaser__medium-wrapper', // teaser--with-medium do also contain a media-still__image!
                '.teaser__longform-medium',
                '.meteo-anchorman-image',
                '.onairnav-list__element .teaser__medium' // livestreams in onair-flyout
            ],
            containers = document.querySelectorAll( relevantClasses.join(', ') );

        for (let i = 0; i < containers.length; i++) {
            polyfillObjectFit(containers[i].querySelector('img'));
        }
    }
}

/*
 * TARGETED BROWSER: IE 11
 *
 * This one adds the value of a data-attribute named "ie-fix" to the class-list of this element – if the user agent is IE11.
 */

function addFixedforIEClass() {
    if (navigator.userAgent.indexOf('MSIE')!==-1 || navigator.appVersion.indexOf('Trident/') > 0) {
        const dataAttr = 'data-iefix';
        let elementsToFix = document.querySelectorAll('['+dataAttr+']');

        for(let i = 0; i < elementsToFix.length; ++i) {
            let ieFixClassName = elementsToFix[i].getAttribute(dataAttr);
            elementsToFix[i].classList.add(ieFixClassName);
        }
    }
}

/*
 * TARGETED BROWSER: IE 11
 *
 * IE11 _sometimes_ has problems with scaled images with svg sources. The usual remedies (viewbox, no widths, etc.)
 * didn't help. This is the last resort - hiding the img and setting a background-image on the parent.
 */
function replaceSVGLogoForIE() {
    if (navigator.userAgent.indexOf('MSIE')!==-1 || navigator.appVersion.indexOf('Trident/') > 0) {
        let bu = document.body.getAttribute('data-bu');
        let fixClassNames = (bu === 'rtr') ? 'header-startlink--fixed-for-ie header-startlink--fixed-for-ie-rtr' : 'header-startlink--fixed-for-ie';

        if(document.getElementsByClassName('header-startlink').length > 0) {
            document.getElementsByClassName('header-startlink')[0].classList.add(fixClassNames);
        }
    }
}

/*
 * TARGETED BROWSER: IE 11
 *
 * polyfill for findIndex.
 * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex
 */
if (!Array.prototype.findIndex) {
    Object.defineProperty(Array.prototype, 'findIndex', {
        value: function(predicate) {
            // 1. Let O be ? ToObject(this value).
            if (this == null) {
                throw new TypeError('"this" is null or not defined');
            }

            let o = Object(this);

            // 2. Let len be ? ToLength(? Get(O, "length")).
            let len = o.length >>> 0;

            // 3. If IsCallable(predicate) is false, throw a TypeError exception.
            if (typeof predicate !== 'function') {
                throw new TypeError('predicate must be a function');
            }

            // 4. If thisArg was supplied, let T be thisArg; else let T be undefined.
            let thisArg = arguments[1];

            // 5. Let k be 0.
            let k = 0;

            // 6. Repeat, while k < len
            while (k < len) {
                // a. Let Pk be ! ToString(k).
                // b. Let kValue be ? Get(O, Pk).
                // c. Let testResult be ToBoolean(? Call(predicate, T, « kValue, k, O »)).
                // d. If testResult is true, return k.
                let kValue = o[k];
                if (predicate.call(thisArg, kValue, k, o)) {
                    return k;
                }
                // e. Increase k by 1.
                k++;
            }

            // 7. Return -1.
            return -1;
        },
        configurable: true,
        writable: true
    });
}
<|endoftext|>


<commit_before>/*
 * This script goes through your Gmail Inbox and finds recent emails where you
 * were the last respondent. It applies a nice label to them, so you can
 * see them in Priority Inbox or do something else.
 *
 * To remove and ignore an email thread, just remove the unrespondedLabel and
 * apply the ignoreLabel.
 *
 * This is most effective when paired with a time-based script trigger.
 *
 * For installation instructions, read this blog post:
 * http://jonathan-kim.com/2013/Gmail-No-Response/
 */

// Edit these to your liking.
var unrespondedLabel = 'No Response',
    ignoreLabel = 'Ignore No Response',
    minDays = 5,
    maxDays = 14;

function main() {
  processUnresponded();
  cleanUp();
}

function processUnresponded() {
  var threads = GmailApp.search('is:sent from:me -in:chats older_than:' + minDays + 'd newer_than:' + maxDays + 'd'),
      numUpdated = 0,
      minDaysAgo = new Date();

  minDaysAgo.setDate(minDaysAgo.getDate() - minDays);

  // Filter threads where I was the last respondent.
  for (var i = 0; i < threads.length; i++) {
    var thread = threads[i],
        messages = thread.getMessages(),
        lastMessage = messages[messages.length - 1],
        lastFrom = lastMessage.getFrom(),
        lastTo = lastMessage.getTo(),  // I don't want to hear about it when I am sender and receiver
        lastMessageIsOld = lastMessage.getDate().getTime() < minDaysAgo.getTime();

    if (isMe(lastFrom) && !isMe(lastTo) && lastMessageIsOld && !threadHasLabel(thread, ignoreLabel)) {
      markUnresponded(thread);
      numUpdated++;
    }
  }

  Logger.log('Updated ' + numUpdated + ' messages.');
}

function isMe(fromAddress) {
  var addresses = getEmailAddresses();
  for (i = 0; i < addresses.length; i++) {
    var address = addresses[i],
        r = RegExp(address, 'i');

    if (r.test(fromAddress)) {
      return true;
    }
  }

  return false;
}

function getEmailAddresses() {
  // Cache email addresses to cut down on API calls.
  if (!this.emails) {
    Logger.log('No cached email addresses. Fetching.');
    var me = Session.getActiveUser().getEmail(),
        emails = GmailApp.getAliases();

    emails.push(me);
    this.emails = emails;
  }
  Logger.log('Found ' + this.emails.length + ' email addresses that belong to you.');
  return this.emails;
}

function threadHasLabel(thread, labelName) {
  var labels = thread.getLabels();

  for (i = 0; i < labels.length; i++) {
    var label = labels[i];

    if (label.getName() == labelName) {
      return true;
    }
  }

  return false;
}

function markUnresponded(thread) {
  var label = getLabel(unrespondedLabel);
  label.addToThread(thread);
}

function getLabel(labelName) {
  // Cache the labels.
  this.labels = this.labels || {};
  label = this.labels[labelName];

  if (!label) {
    Logger.log('Could not find cached label "' + labelName + '". Fetching.', this.labels);

    var label = GmailApp.getUserLabelByName(labelName);

    if (label) {
      Logger.log('Label exists.');
    } else {
      Logger.log('Label does not exist. Creating it.');
      label = GmailApp.createLabel(labelName);
    }
    this.labels[labelName] = label;
  }
  return label;
}

function cleanUp() {
  var label = getLabel(unrespondedLabel),
      iLabel = getLabel(ignoreLabel),
      threads = label.getThreads(),
      numExpired = 0,
      twoWeeksAgo = new Date();

  twoWeeksAgo.setDate(twoWeeksAgo.getDate() - maxDays);

  if (!threads.length) {
    Logger.log('No threads with that label');
    return;
  } else {
    Logger.log('Processing ' + threads.length + ' threads.');
  }

  for (i = 0; i < threads.length; i++) {
    var thread = threads[i],
        lastMessageDate = thread.getLastMessageDate();

    // Remove all labels from expired threads.
    if (lastMessageDate.getTime() < twoWeeksAgo.getTime()) {
      numExpired++;
      Logger.log('Thread expired');
      label.removeFromThread(thread);
      iLabel.removeFromThread(thread);
    } else {
      Logger.log('Thread not expired');
    }
  }
  Logger.log(numExpired + ' unresponded messages expired.');
}
<commit_msg>Support more units of time and drastic speed improvements.<commit_after>/*
 * This script goes through your Gmail Inbox and finds recent emails where you
 * were the last respondent. It applies a nice label to them, so you can
 * see them in Priority Inbox or do something else.
 *
 * To remove and ignore an email thread, just remove the unrespondedLabel and
 * apply the ignoreLabel.
 *
 * This is most effective when paired with a time-based script trigger.
 *
 * For installation instructions, read this blog post:
 * http://jonathan-kim.com/2013/Gmail-No-Response/
 */

// Edit these to your liking.
var unrespondedLabel = 'No Response',
    ignoreLabel = 'Ignore No Response',
    minTime = '5d',   // 5 days
    maxTime = '14d';  // 14 days

// Mapping of Gmail search time units to milliseconds.
var UNIT_MAPPING = {
    h: 36e5,    // Hours
    d: 864e5,   // Days
    w: 6048e5,  // Weeks
    m: 263e7,   // Months
    y: 3156e7   // Years
};

function main() {
  Logger.log('Start.');
  processUnresponded();
  Logger.log('Finished processing.');
  cleanUp();
  Logger.log('Finished all.');
}

function processUnresponded() {
  var threads = GmailApp.search('is:sent from:me -in:chats older_than:' + minTime + ' newer_than:' + maxTime),
      threadMessages = GmailApp.getMessagesForThreads(threads),
      unrespondedThreads = [],
      minTimeAgo = new Date();

  Logger.log('now: ' + minTimeAgo.toString());
  minTimeAgo.setTime(subtract(minTimeAgo, minTime));
  Logger.log('minTimeAgo: ' + minTimeAgo.toString());

  Logger.log('Processing ' + threads.length + ' threads.');

  // Filter threads where I was the last respondent.
  threadMessages.forEach(function(messages, i) {
    var thread = threads[i],
        lastMessage = messages[messages.length - 1],
        lastFrom = lastMessage.getFrom(),
        lastTo = lastMessage.getTo(),  // I don't want to hear about it when I am sender and receiver
        lastMessageIsOld = lastMessage.getDate().getTime() < minTimeAgo.getTime();

    if (isMe(lastFrom) && !isMe(lastTo) && lastMessageIsOld && !threadHasLabel(thread, ignoreLabel)) {
      unrespondedThreads.push(thread);
    }
  })

  // Mark unresponded in bulk.
  markUnresponded(unrespondedThreads);
  Logger.log('Updated ' + unrespondedThreads.length + ' messages.');
}

function subtract(date, timeStr) {
  // Takes a date object and subtracts a Gmail-style time string (e.g. '5d').
  // Returns a new date object.
  var re = /^([0-9]+)([a-zA-Z]+)$/,
      parts = re.exec(timeStr),
      val = parts && parts[1],
      unit = parts && parts[2],
      ms = UNIT_MAPPING[unit];

  return date.getTime() - (val * ms);
}

function isMe(fromAddress) {
  var addresses = getEmailAddresses();
  for (i = 0; i < addresses.length; i++) {
    var address = addresses[i],
        r = RegExp(address, 'i');

    if (r.test(fromAddress)) {
      return true;
    }
  }

  return false;
}

function getEmailAddresses() {
  // Cache email addresses to cut down on API calls.
  if (!this.emails) {
    Logger.log('No cached email addresses. Fetching.');
    var me = Session.getActiveUser().getEmail(),
        emails = GmailApp.getAliases();

    emails.push(me);
    this.emails = emails;
    Logger.log('Found ' + this.emails.length + ' email addresses that belong to you.');
  }
  return this.emails;
}

function threadHasLabel(thread, labelName) {
  var labels = thread.getLabels();

  for (i = 0; i < labels.length; i++) {
    var label = labels[i];

    if (label.getName() == labelName) {
      return true;
    }
  }

  return false;
}

function markUnresponded(threads) {
  var label = getLabel(unrespondedLabel);
  label.addToThreads(threads);
}

function getLabel(labelName) {
  // Cache the labels.
  this.labels = this.labels || {};
  label = this.labels[labelName];

  if (!label) {
    Logger.log('Could not find cached label "' + labelName + '". Fetching.', this.labels);

    var label = GmailApp.getUserLabelByName(labelName);

    if (label) {
      Logger.log('Label exists.');
    } else {
      Logger.log('Label does not exist. Creating it.');
      label = GmailApp.createLabel(labelName);
    }
    this.labels[labelName] = label;
  }
  return label;
}

function cleanUp() {
  var label = getLabel(unrespondedLabel),
      iLabel = getLabel(ignoreLabel),
      threads = label.getThreads(),
      expiredThreads = [],
      twoWeeksAgo = new Date();

  twoWeeksAgo.setTime(subtract(twoWeeksAgo, maxTime));

  if (!threads.length) {
    Logger.log('No threads with that label');
    return;
  } else {
    Logger.log('Processing ' + threads.length + ' threads.');
  }

  threads.forEach(function(thread) {
    var lastMessageDate = thread.getLastMessageDate();

    // Remove all labels from expired threads.
    if (lastMessageDate.getTime() < twoWeeksAgo.getTime()) {
      Logger.log('Thread expired');
      expiredThreads.push(thread);
    } else {
      Logger.log('Thread not expired');
    }
  });

  label.removeFromThreads(expiredThreads);
  iLabel.removeFromThreads(expiredThreads);
  Logger.log(expiredThreads.length + ' unresponded messages expired.');
}
<|endoftext|>


<commit_before>define(["../_StoreMixin", "dojo/_base/declare", "dojo/_base/lang", "dojo/on", "dojo/query", "dojo/string", "dojo/_base/Deferred", "put-selector/put", "dojo/i18n!./nls/pagination", "xstyle/css!../css/extensions/Pagination.css"],
function(_StoreMixin, declare, lang, on, query, string, Deferred, put, i18n){
	return declare([_StoreMixin], {
		// summary:
		//		An extension for adding discrete pagination to a List or Grid.

		// rowsPerPage: Number
		//		Number of rows (items) to show on a given page.
		rowsPerPage: 10,

		showFooter: true,
		// pagingTextBox: Boolean
		// 		Indicate whether or not to show a textbox for paging
		pagingTextBox: false,
		// previousNextArrows: Boolean
		// 		Indicate whether or not to show the previous and next arrow links
		previousNextArrows: true,
		// firstLastArrows: Boolean
		// 		Indicate whether or not to show the first and last arrow links
		firstLastArrows: false,
		_currentPage: 1,
		_total: 0,
		// pagingLinks: Number|Boolean
		// 		The number of page links to show on each side of the current page
		//		Set to false to disable the page links
		pagingLinks: 3,
		// pageSizeOptions: Array[Number]
		// 		This provides options for different page sizes in a dropdown. If it is empty (default)
		// 		no page size dropdown will be displayed
		pageSizeOptions: [],

		buildRendering: function(){
			var grid = this;

			this.inherited(arguments);

			// add pagination to footer
			var paginationNode = this.paginationNode =
					put(this.footerNode, "div.dgrid-pagination"),
				statusNode = this.paginationStatusNode =
					put(paginationNode, "div.dgrid-status"),
				pageSizeOptions = this.pageSizeOptions;
			if(pageSizeOptions.length){
				var sizeSelect = put(paginationNode, 'select.dgrid-page-size');
				for(var i = 0; i < pageSizeOptions.length; i++){
					put(sizeSelect, 'option', pageSizeOptions[i], {value: pageSizeOptions[i]});
				}
				on(sizeSelect, "change", function(){
					grid.rowsPerPage = +sizeSelect.value;
					grid.gotoPage(1);
				});
			}

			var navigationNode = this.paginationNavigationNode =
					put(paginationNode, "div.dgrid-navigation");

			var navigationNode = this.paginationNavigationNode,
				currentPage = this._currentPage,
				previousNextLinks = this.previousNextLinks,
				pagingLinks = this.pagingLinks,
				end = this._total / this.rowsPerPage,
				pagingTextBoxHandle = this._pagingTextBoxHandle;
			if(this.firstLastArrows){
				// create a previous link
				put(navigationNode,  'a[href=javascript:].dgrid-first', '«');
			}
			if(this.previousNextArrows){
				// create a previous link
				put(navigationNode,  'a[href=javascript:].dgrid-previous', '‹');
			}
			var grid = this;
			this.paginationLinksNode = put(navigationNode, "span.dgrid-pagination-links");
			if(this.previousNextArrows){
				// create a next link
				put(navigationNode, 'a[href=javascript:].dgrid-next', '›');
			}
			if(this.firstLastArrows){
				// create a previous link
				put(navigationNode,  'a[href=javascript:].dgrid-last', '»');
			}

			on(navigationNode, "a:click", function(evt){
				if(grid._isLoading){ return; }

				var curr = grid._currentPage,
					max = Math.ceil(grid._total / grid.rowsPerPage);

				// determine navigation target based on clicked link's class
				if(this.className == "dgrid-page-link"){
					grid.gotoPage(+this.innerHTML, true); // the innerHTML has the page number
				}
				if(this.className == "dgrid-first"){
					grid.gotoPage(1);
				}else if(this.className == "dgrid-previous"){
					if(curr > 1){ grid.gotoPage(curr - 1); }
				}else if(this.className == "dgrid-next"){
					if(curr < max){ grid.gotoPage(curr + 1); }
				}else if(this.className == "dgrid-last"){
					grid.gotoPage(max);
				}
			});

		},
		_updateNavigation: function(focusLink){
			// summary:
			//		Update status and navigation controls based on total count from query
			function pageLink(page){
				var link;
				if(grid.pagingTextBox && page == currentPage){
					// use a paging text box if enabled instead of just a number
					grid._pagingTextBoxHandle = on(link = put(linksNode, 'input.dgrid-page-input[type=text][value=$]', currentPage), "change", function(){
						grid.gotoPage(+this.value, true);
					});
				}else{
					// normal link
					link = put(linksNode, 'a[href=javascript:]' + (page == currentPage ? '.dgrid-page-disabled' : '') + '.dgrid-page-link', page);
				}
				if(page == currentPage && focusLink){
					// focus on it if we are supposed to retain the focus
					link.focus();
				}
			}
			var grid = this,
				linksNode = this.paginationLinksNode,
				currentPage = this._currentPage,
				pagingLinks = this.pagingLinks,
				paginationNavigationNode = this.paginationNavigationNode,
				end = Math.ceil(this._total / this.rowsPerPage),
				pagingTextBoxHandle = this._pagingTextBoxHandle;
			pagingTextBoxHandle && pagingTextBoxHandle.remove(); // remove the old handler if it has been created
			linksNode.innerHTML = "";
			query(".dgrid-first, .dgrid-previous", paginationNavigationNode).forEach(function(link){
				put(link, (currentPage == 1 ? "." : "!") + "dgrid-page-disabled");
			});
			query(".dgrid-last, .dgrid-next", paginationNavigationNode).forEach(function(link){
				put(link, (currentPage == end ? "." : "!") + "dgrid-page-disabled");
			});

			if(pagingLinks){
				// always include the first page (back to the beginning)
				pageLink(1);
				var start = currentPage - pagingLinks;
				if(start > 2) {
					// visual indication of skipped page links
					put(linksNode, 'span.dgrid-page-skip', '...');
				}else{
					start = 2;
				}
				// now iterate through all the page links we should show
				for(var i = start; i < Math.min(currentPage + pagingLinks + 1, end); i++){
					pageLink(i);
				}
				if(currentPage + pagingLinks + 1 < end){
					put(linksNode, 'span.dgrid-page-skip', '...');
				}
				// last link
				pageLink(end);
			}else if(grid.pagingTextBox){
				// the pageLink will create our textbox for us
				pageLink(currentPage);
			}
		},

		refresh: function(){
			if(!this.store){
				throw new Error("Pagination requires a store to operate.");
			}
			this.inherited(arguments);
			// reset to first page
			this.gotoPage(1);
		},

		gotoPage: function(page, focusLink){
			// summary:
			//		Loads the given page.  Note that page numbers start at 1.
			var grid = this;
			this._trackError(function(){
				var count = grid.rowsPerPage,
					start = (page - 1) * count,
					options = lang.delegate(grid.queryOptions || {}, {
						start: start,
						count: count
					}),
					results,
					contentNode = grid.contentNode,
					rows = grid._rowIdToObject,
					substrLen = 5 + grid.id.length, // trimmed from front of row IDs
					r, loadingNode;

				if(grid.sortOrder){ options.sort = grid.sortOrder; }

				// remove any currently-rendered rows
				for(r in rows){
					grid.row(r.substr(substrLen)).remove();
				}
				grid._rowIdToObject = {};
				contentNode.innerHTML = "";

				loadingNode = put(contentNode, "div.dgrid-loading");

				// set flag to deactivate pagination event handlers until loaded
				grid._isLoading = true;

				// Run new query and pass it into renderArray
				results = grid.store.query(grid.query, options);

				Deferred.when(results.total, function(total){
					// update status text based on now-current page and total
					grid.paginationStatusNode.innerHTML = string.substitute(i18n.status, {
						start: start + 1,
						end: Math.min(total, start + count),
						total: total
					});
					grid._total = total;
					grid._currentPage = page;
					grid._updateNavigation(focusLink);
				});

				return Deferred.when(grid.renderArray(results, loadingNode, options), function(trs){
					put(loadingNode, "!");
					delete grid._isLoading;
					// reset scroll position now that new page is loaded
					grid.bodyNode.scrollTop = 0;
				}, function(error){
					// enable loading again before throwing the error
					delete grid._isLoading;
					throw error;
				});
			});
		}
	});
});<commit_msg>Pagination: cleanup and a couple of behavior fixes for focus/onchange<commit_after>define(["../_StoreMixin", "dojo/_base/declare", "dojo/_base/lang", "dojo/on", "dojo/query", "dojo/string", "dojo/_base/Deferred", "put-selector/put", "dojo/i18n!./nls/pagination", "xstyle/css!../css/extensions/Pagination.css"],
function(_StoreMixin, declare, lang, on, query, string, Deferred, put, i18n){
	return declare([_StoreMixin], {
		// summary:
		//		An extension for adding discrete pagination to a List or Grid.

		// rowsPerPage: Number
		//		Number of rows (items) to show on a given page.
		rowsPerPage: 10,

		// pagingTextBox: Boolean
		// 		Indicates whether or not to show a textbox for paging.
		pagingTextBox: false,
		// previousNextArrows: Boolean
		// 		Indicates whether or not to show the previous and next arrow links.
		previousNextArrows: true,
		// firstLastArrows: Boolean
		// 		Indicates whether or not to show the first and last arrow links.
		firstLastArrows: false,

		// pagingLinks: Number|Boolean
		// 		The number of page links to show on each side of the current page
		//		Set to false to disable page links.
		pagingLinks: 3,
		// pageSizeOptions: Array[Number]
		// 		This provides options for different page sizes in a dropdown.
		//		If it is empty (default), no page size dropdown will be displayed.
		pageSizeOptions: [],

		showFooter: true,
		_currentPage: 1,
		_total: 0,

		buildRendering: function(){
			var grid = this;

			this.inherited(arguments);

			// add pagination to footer
			var paginationNode = this.paginationNode =
					put(this.footerNode, "div.dgrid-pagination"),
				statusNode = this.paginationStatusNode =
					put(paginationNode, "div.dgrid-status"),
				pageSizeOptions = this.pageSizeOptions;
			if(pageSizeOptions.length){
				var sizeSelect = put(paginationNode, 'select.dgrid-page-size');
				for(var i = 0; i < pageSizeOptions.length; i++){
					put(sizeSelect, 'option', pageSizeOptions[i], {value: pageSizeOptions[i]});
				}
				on(sizeSelect, "change", function(){
					grid.rowsPerPage = +sizeSelect.value;
					grid.gotoPage(1);
				});
			}

			var navigationNode = this.paginationNavigationNode =
					put(paginationNode, "div.dgrid-navigation");

			var navigationNode = this.paginationNavigationNode,
				currentPage = this._currentPage,
				previousNextLinks = this.previousNextLinks,
				pagingLinks = this.pagingLinks,
				end = this._total / this.rowsPerPage,
				pagingTextBoxHandle = this._pagingTextBoxHandle;
			if(this.firstLastArrows){
				// create a first-page link
				put(navigationNode,  'a[href=javascript:].dgrid-first', '«');
			}
			if(this.previousNextArrows){
				// create a previous link
				put(navigationNode,  'a[href=javascript:].dgrid-previous', '‹');
			}
			var grid = this;
			this.paginationLinksNode = put(navigationNode, "span.dgrid-pagination-links");
			if(this.previousNextArrows){
				// create a next link
				put(navigationNode, 'a[href=javascript:].dgrid-next', '›');
			}
			if(this.firstLastArrows){
				// create a last-page link
				put(navigationNode,  'a[href=javascript:].dgrid-last', '»');
			}

			on(navigationNode, "a:click", function(evt){
				var cls = this.className,
					curr, max;

				if(grid._isLoading || cls.indexOf("dgrid-page-disabled") > -1){
					return;
				}

				curr = grid._currentPage;
				max = Math.ceil(grid._total / grid.rowsPerPage);

				// determine navigation target based on clicked link's class
				if(cls == "dgrid-page-link"){
					grid.gotoPage(+this.innerHTML, true); // the innerHTML has the page number
				}
				if(cls == "dgrid-first"){
					grid.gotoPage(1);
				}else if(cls == "dgrid-previous"){
					if(curr > 1){ grid.gotoPage(curr - 1); }
				}else if(cls == "dgrid-next"){
					if(curr < max){ grid.gotoPage(curr + 1); }
				}else if(cls == "dgrid-last"){
					grid.gotoPage(max);
				}
			});

		},
		_updateNavigation: function(focusLink){
			// summary:
			//		Update status and navigation controls based on total count from query

			var grid = this,
				linksNode = this.paginationLinksNode,
				currentPage = this._currentPage,
				pagingLinks = this.pagingLinks,
				paginationNavigationNode = this.paginationNavigationNode,
				end = Math.ceil(this._total / this.rowsPerPage),
				pagingTextBoxHandle = this._pagingTextBoxHandle;

			function pageLink(page){
				var link;
				if(grid.pagingTextBox && page == currentPage){
					// use a paging text box if enabled instead of just a number
					link = put(linksNode, 'input.dgrid-page-input[type=text][value=$]', currentPage);
					grid._pagingTextBoxHandle = on(link, "change", function(evt){
						var value = +this.value;
						if(!isNaN(value) && value > 0 && value <= end){
							grid.gotoPage(+this.value, true);
						}
					});
				}else{
					// normal link
					link = put(linksNode,
						'a[href=javascript:]' + (page == currentPage ? '.dgrid-page-disabled' : '') + '.dgrid-page-link',
						page);
				}
				if(page == currentPage && focusLink){
					// focus on it if we are supposed to retain the focus
					link.focus();
				}
			}

			pagingTextBoxHandle && pagingTextBoxHandle.remove(); // remove the old handler if it has been created
			linksNode.innerHTML = "";
			query(".dgrid-first, .dgrid-previous", paginationNavigationNode).forEach(function(link){
				put(link, (currentPage == 1 ? "." : "!") + "dgrid-page-disabled");
			});
			query(".dgrid-last, .dgrid-next", paginationNavigationNode).forEach(function(link){
				put(link, (currentPage == end ? "." : "!") + "dgrid-page-disabled");
			});

			if(pagingLinks){
				// always include the first page (back to the beginning)
				pageLink(1);
				var start = currentPage - pagingLinks;
				if(start > 2) {
					// visual indication of skipped page links
					put(linksNode, 'span.dgrid-page-skip', '...');
				}else{
					start = 2;
				}
				// now iterate through all the page links we should show
				for(var i = start; i < Math.min(currentPage + pagingLinks + 1, end); i++){
					pageLink(i);
				}
				if(currentPage + pagingLinks + 1 < end){
					put(linksNode, 'span.dgrid-page-skip', '...');
				}
				// last link
				pageLink(end);
			}else if(grid.pagingTextBox){
				// the pageLink will create our textbox for us
				pageLink(currentPage);
			}
		},

		refresh: function(){
			if(!this.store){
				throw new Error("Pagination requires a store to operate.");
			}
			this.inherited(arguments);
			// reset to first page
			this.gotoPage(1);
		},

		gotoPage: function(page, focusLink){
			// summary:
			//		Loads the given page.  Note that page numbers start at 1.
			var grid = this;
			this._trackError(function(){
				var count = grid.rowsPerPage,
					start = (page - 1) * count,
					options = lang.delegate(grid.queryOptions || {}, {
						start: start,
						count: count
					}),
					results,
					contentNode = grid.contentNode,
					rows = grid._rowIdToObject,
					substrLen = 5 + grid.id.length, // trimmed from front of row IDs
					r, loadingNode;

				if(grid.sortOrder){ options.sort = grid.sortOrder; }

				// remove any currently-rendered rows
				for(r in rows){
					grid.row(r.substr(substrLen)).remove();
				}
				grid._rowIdToObject = {};
				contentNode.innerHTML = "";

				loadingNode = put(contentNode, "div.dgrid-loading");

				// set flag to deactivate pagination event handlers until loaded
				grid._isLoading = true;

				// Run new query and pass it into renderArray
				results = grid.store.query(grid.query, options);

				return Deferred.when(grid.renderArray(results, loadingNode, options), function(trs){
					put(loadingNode, "!");
					delete grid._isLoading;
					// reset scroll position now that new page is loaded
					grid.bodyNode.scrollTop = 0;

					Deferred.when(results.total, function(total){
						// update status text based on now-current page and total
						grid.paginationStatusNode.innerHTML = string.substitute(i18n.status, {
							start: start + 1,
							end: Math.min(total, start + count),
							total: total
						});
						grid._total = total;
						grid._currentPage = page;

						// It's especially important that _updateNavigation is called only
						// after renderArray is resolved as well (to prevent jumping)
						grid._updateNavigation(focusLink);
					});
				}, function(error){
					// enable loading again before throwing the error
					delete grid._isLoading;
					throw error;
				});
			});
		}
	});
});<|endoftext|>