* {
* controllerOne: [ mixin1, mixin2 ],
* controllerTwo: [],
* controllerThree: []
* }
*
*
* @returns {string}
*/
_getControllersAndMixins = function (controllerString) {
var controllers = {};
var pieces = controllerString.split(',');
for( var i = 0; i < pieces.length; i++ ){
pieces[i] = pieces[i].trim();
// Fix case issues on user-submitted content
if( !_.isUndefined( _controllerCaseMap[ pieces[i] ] ) ){
pieces[i] = _controllerCaseMap[ pieces[i] ];
}
if( pieces[i].indexOf('(') === -1 ){
controllers[ pieces[i] ] = [];
continue;
}
var p = pieces[i].match( /([a-zA-Z0-9.]+)\((.+?)\)/i );
var mixinPieces = [];
_.each( p[2].split(';'), function (val) {
mixinPieces.push( val.trim() );
});
controllers[ p[1] ] = mixinPieces;
}
return controllers;
},
/**
* Returns an incremental controller ID
* Controller IDs are used to enable controllers to identify events that they
* emitted themselves.
*
* @returns {string}
*/
getInstanceID = function () {
return 'ipscontroller' + (++instanceID);
},
/**
* Allows an element to be cleaned externally. If the element passed is not a controller scope,
* it'll search down one level of the DOM to find controllers.
*
* @param {element} elem The element to clean
* @returns {string}
*/
cleanContentsOf = function (elem) {
Debug.log('Cleaning contents of controller');
$( elem ).find('[data-controller]')
.each( function () {
var loopController = $( this );
var controllers = loopController.data( '_controllerObjs' ) || [];
if( controllers.length ){
loopController.data('_controllerObjs', []);
for( var i = 0; i < controllers.length; i++ ){
controllers[i]._destroy.apply( controllers[i] );
delete controllers[i];
}
}
});
// Remove any widgets that exist in this elem
ips.ui.destructAllWidgets( $( elem ) );
},
/**
* Initializes a controller instance by creating a new function, extending it with
* the controller methods then initializing it on the relevant dom node
*
* @param {element} elem The element that will form the scope of this controller
* @param {string} controllerID ID of this controller
* @returns {void}
*/
initControllerOnElem = function (elem, controllerID, mixins) {
if( !_controllers[ controllerID ] ){
Debug.error("Controller '" + controllerID + "' has not been registered");
return;
}
if( _.isUndefined( $( elem ).data('_controllers') )){
$( elem ).data('_controllers', []);
}
$( elem ).data('_controllers').push( controllerID );
if( _.isUndefined( _prototypes[ controllerID ] ) ){
// Fetch our controller prototype
_prototypes[ controllerID ] = getBaseController();
// Extend with our specific controller methods
$.extend( true, _prototypes[ controllerID ].prototype, _controllers[ controllerID ] );
}
// And init
if( _.isUndefined( $( elem ).data( '_controllerObjs' ) ) ){
$( elem ).data( '_controllerObjs', [] );
}
var controllers = $( elem ).data( '_controllerObjs' );
var obj = new _prototypes[ controllerID ](elem, controllerID);
controllers.push( obj );
// Any mixins?
// Auto mixins first
if( !_.isUndefined( _autoMixins[ controllerID ] ) && _.size( _autoMixins[ controllerID ] ) ){
_.each( _autoMixins[ controllerID ], function (val, key) {
_autoMixins[ controllerID ][ key ].call( obj );
});
}
// Then the manually-specified ones
if( mixins.length ){
for( var i = 0; i < mixins.length; i++ ){
if( !_.isUndefined( _manualMixins[ controllerID ] ) && !_.isUndefined( _manualMixins[ controllerID ][ mixins[i] ] ) ){
_manualMixins[ controllerID ][ mixins[i] ].call( obj );
}
}
}
if( _.isFunction( obj.initialize ) ){
obj.initialize.call( obj );
}
$( elem ).removeData( '_controller' + controllerID );
$( document ).trigger( 'controllerReady', {
controllerID: obj.controllerID,
controllerType: obj.controllerType,
controllerElem: elem
});
},
/**
* Finds controllers within a node that have an ID matching the provided name
* Wildcard character * supported at the front or end of the controller parameter
*
* @param {string} controller Controller name to find
* @param {element} node Optional node to search in (document by default)
* @returns {function}
*/
_findSubControllers = function (controller, node) {
var results = [];
node = ( node && ( _.isElement( node ) || node.jquery ) ) ? node : document;
if( controller.indexOf('*') === -1 ){
results = $( node ).find('[data-controller*="' + controller + '"]');
} else {
var pieces = controller.split('.');
if( pieces[0] == '*' ){
pieces.shift();
results = $( node ).find('[data-controller$="' + pieces.join('.') + '"]');
} else if( pieces[ pieces.length - 1 ] == '*' ){
pieces.pop();
results = $( node ).find('[data-controller^="' + pieces.join('.') + '"]');
}
}
return results;
},
/**
* Returns a new function that will form our controller prototype
*
* @returns {function}
*/
getBaseController = function () {
/** Base controller definition */
var baseController = function (scope, type) {
this.controllerType = type;
this.controllerID = getInstanceID();
this.scope = $( scope );
this._eventListeners = [];
var self = this;
// Advice methods - inspired by http://javascriptweblog.wordpress.com/2011/05/31/a-fresh-look-at-javascript-mixins/
// and Twitter Flight
var adviceFuncs = {
before: function (baseFn, newFn) {
return function () {
newFn.apply( this, arguments );
return baseFn.apply( this, arguments );
};
},
after: function (baseFn, newFn) {
return function () {
var toReturn = baseFn.apply( this, arguments );
newFn.apply( this, arguments );
return toReturn;
}
},
around: function (baseFn, newFn) {
return function () {
var args = ips.utils.argsToArray( arguments );
args.unshift( baseFn.bind( this ) );
return newFn.apply( this, args );
}
}
}
_.each( ['before', 'after', 'around'], _.bind( function (type) {
this[ type ] = function (base, fn) {
if( _.isUndefined( this[ base ] ) || !_.isFunction( this[ base ] ) ){
Debug.log( "Method '" + base + '" is not present in controller ' + this.controllerID );
return;
}
// Replace our base method with a wrapped version
this[ base ] = adviceFuncs[ type ]( this[ base ], fn );
};
}, this ) );
//Debug.info("Initialized " + this.controllerID + " of type " + this.controllerType);
};
baseController.method('_destroy', function () {
Debug.log( 'Destroyed instance ' + this.controllerID + ' of ' + this.controllerType );
// Remove each event listener that was created in this controller
if( this._eventListeners.length ){
for( var i = 0; i < this._eventListeners.length; i++ ){
var data = this._eventListeners[i];
if( data['delegate'] ){
data['elem'].off( data['ev'], data['delegate'], data['fn'] );
} else {
data['elem'].off( data['ev'], data['fn'] );
}
}
}
if( _.isFunction( this.destroy ) ){
this.destroy.call( this );
}
// Remove reference to scope so that GC can do its thing
this.scope = null;
});
// Searches for controllers within the current, triggers a destroy event and deletes the controller objs
baseController.method('cleanContents', function () {
Debug.log('Cleaning contents of controller');
this.scope.find('[data-controller]')
.each( function () {
var loopController = $( this );
var controllers = loopController.data( '_controllerObjs' ) || [];
if( controllers.length ){
loopController.data('_controllerObjs', []);
for( var i = 0; i < controllers.length; i++ ){
controllers[i]._destroy.apply( controllers[i] );
delete controllers[i];
}
}
});
// Remove any widgets that exist in this elem
ips.ui.destructAllWidgets( this.scope );
});
baseController.method('trigger', function (elem, ev, data) {
// Convert silly arguments object to an array
var args = ips.utils.argsToArray( arguments );
elem = ( !_.isElement( elem ) && !elem.jquery ) ? this.scope : $( args.shift() );
ev = args[0];
data = args[1] || {};
// Add our origin to the event
if( !data.stack ){
data.stack = [];
}
data.stack.push( 'controllers.' + this.controllerType + '.' + this.controllerID );
elem.trigger( ev, data );
});
baseController.method('on', function (elem, ev, delegate, fn) {
// Convert silly arguments object to an array
var args = ips.utils.argsToArray( arguments );
// Reconfigure our args as necessary
elem = ( !_.isElement( elem ) && elem != document && elem != window ) ? this.scope : $( args.shift() );
ev = args[0];
fn = ( args.length == 3 ) ? args[2] : args[1];
delegate = ( args.length == 3 ) ? args[1] : undefined;
if( !_.isFunction( fn ) ){
Debug.warn("Callback function for " + ev + " doesn't exist in " + this.controllerType
+ " (" + this.controllerID + ")");
return;
}
// Bind our callback to the controller
fn = _.bind( fn, this );
// Set up the event
if( delegate ){
elem.on( ev, delegate, fn );
this._eventListeners.push({
elem: elem,
event: ev,
delegate: delegate,
fn: fn
});
} else {
elem.on( ev, fn );
this._eventListeners.push({
elem: elem,
event: ev,
fn: fn
});
}
});
baseController.method('triggerOn', function (controller, ev, data) {
var toTrigger = _findSubControllers( controller, this.scope );
if( !toTrigger.length ){
return;
}
data = data || {};
// Add our origin to the event
if( !data.stack ){
data.stack = [];
}
data.stack.push( 'controllers.' + this.controllerType + '.' + this.controllerID );
toTrigger.trigger( ev, data );
});
return baseController;
};
return {
initControllerOnElem: initControllerOnElem,
register: register,
mixin: mixin,
isRegistered: isRegistered,
init: init,
cleanContentsOf: cleanContentsOf
};
});
}( jQuery, _ ));
]]>" + selectedText + ""; } else if( !selectedText.startsWith('<') ) { // If the user selects a bunch of text that contains HTML, the browser will automatically wrap it for us. But if the user // just selects some plain text, that's all we get. So in that case, we'll wrap it ourselves. selectedText = '
' + selectedText + '
'; } if( this._selectedText == selectedText ){ return; } this._selectedText = selectedText; this._showQuoteTooltip(); }, /** * Builds & shows the selective quoting tooltip, displaying it just above the user's cursor * * @returns {void} */ _showQuoteTooltip: function () { var selection = ips.utils.selection.getSelection(); var range = ips.utils.selection.getRange( this.scope.find('[data-role="commentContent"]') ); var tooltip = this.scope.find('[data-role="inlineQuoteTooltip"]'); var scopeOffset = this.scope.offset(); var position = { left: 0, top: 0 }; if( range === false || !_.isObject( range ) || _.isUndefined( range.type ) ){ // No selection found Debug.log("No selection found"); return; } // Create the new tooltip if needed if( !tooltip.length ){ this.scope.append( ips.templates.render('core.selection.quote', { direction: 'bottom'//ips.utils.events.isTouchDevice() ? 'bottom' : 'top' }) ); tooltip = this.scope.find('[data-role="inlineQuoteTooltip"]'); $( document ).on( 'click dblclick', this._clickHandler ); } if ( range.type === 'outside' ){ // Selection was beyond our content area, so we need to limit it to end of the content // Get the bounding of the selection to use as the basis of our tooltip position var boundingBox = range.range.getBoundingClientRect(); var offset = this.scope.offset(); position.left = boundingBox.left + ( boundingBox.width / 2 ) + $( window ).scrollLeft() - offset.left; position.top = boundingBox.top + boundingBox.height + $( window ).scrollTop() - offset.top; } else { // Normal selection, inside content, so we can position based on the Range directly // Clone the range, and insert an element containing an invisible character which we'll use // to fetch the position var cloneRange = range.range.cloneRange(); var invisibleElement = document.createElement('span'); invisibleElement.appendChild( document.createTextNode('\ufeff') ); // Collapse the range so that we only care about the end position cloneRange.collapse( false ); // Insert our invisible element into the range cloneRange.insertNode( invisibleElement ); // Get the position of the invisible element we created var tmpPosition = ips.utils.position.getElemPosition( $( invisibleElement ) ); position.left = tmpPosition.absPos.left - scopeOffset.left; position.top = tmpPosition.absPos.top - scopeOffset.top + 25; // Remove the invisible element (fixes #1758) invisibleElement.parentNode.removeChild(invisibleElement); } var tooltipSize = { width: tooltip.show().outerWidth(), height: tooltip.show().outerHeight() }; // Set the position. On touch devices, move it a little further to the left to avoid the OS's touch handle var leftAdjustment = ips.utils.events.isTouchDevice() ? tooltipSize.width : ( tooltipSize.width / 2 ); tooltip.css({ position: 'absolute', left: Math.round( position.left - leftAdjustment ) + 'px', top: Math.round( position.top ) + 'px', zIndex: ips.ui.zIndex() }); // If the tooltip isn't already shown, fade it in if( !tooltip.is(':visible') ){ tooltip.hide().fadeIn('fast'); } else { tooltip.show(); } }, /** * Hide the selective quote tooltip * * @returns {void} */ _hideQuoteTooltip: function () { $( document ).off( 'click dblclick', this._clickHandler ); clearInterval( this._quoteTimeout ); this.scope.find('[data-role="inlineQuoteTooltip"]').fadeOut('fast'); this._selectedText = ''; }, /** * Event handler for clicking 'quote this' in the selective quote tooltip * Triggers an event sent to the comment feed * * @param {event} e Event object * @returns {void} */ quoteSelection: function (e) { e.preventDefault(); this._getQuoteData(); if( this._selectedText ){ this.trigger('quoteComment.comment', { userid: this._quoteData.userid, username: this._quoteData.username, timestamp: this._quoteData.timestamp, contentapp: this._quoteData.contentapp, contenttype: this._quoteData.contenttype, contentclass: this._quoteData.contentclass, contentid: this._quoteData.contentid, contentcommentid: this._quoteData.contentcommentid, quoteHtml: this._selectedText }); } this._hideQuoteTooltip(); }, /** * Triggered when the moderation checkbox is changed * * @param {event} e Event object * @param {object} data Event data object * @returns {void} */ commentCheckbox: function (e) { var checked = $( e.currentTarget ).is(':checked'); this.scope.closest('.ipsComment').toggleClass( 'ipsComment_selected', checked ); this.trigger('checkedComment.comment', { commentID: this._commentID, actions: $( e.currentTarget ).attr('data-actions'), checked: checked }); }, /** * The comment feed has told us we can't support quoting * * @returns {void} */ disableQuoting: function () { this._quotingDisabled = true; this.scope.find('[data-ipsQuote-editor]').remove(); }, /** * Event handler for the Helpful/Unhelpful buttons. * * @param {event} e Event object * @returns {void} */ rateReview: function (e) { e.preventDefault(); var self = this; ips.getAjax()( $( e.currentTarget ).attr('href') ) .done( function (response) { var content = $("(
|<\/p>|\s)*<\/p>$/)) { ips.ui.alert.show({ type: 'alert', icon: 'warn', message: ips.getString('cantEmptyEdit'), subText: ips.getString('cantEmptyEditDesc') }); return; } } var form = this.scope.find('form'); var url = form.attr('action'); var data = form.serialize(); form.find('[data-action="cancelEditComment"]').remove(); form.find('[type="submit"]').prop( 'disabled', true ).text( ips.getString('saving') ); this.trigger( 'saveEditComment.comment', { form: data, url: url, commentID: this._commentID }); }, /** * Model event: something is loading * * @param {event} e Event object * @param {object} data Event data object * @returns {void} */ commentLoading: function (e, data) { if( data.commentID != this._commentID ){ return; } var commentLoading = this.scope.find('[data-role="commentLoading"]'); commentLoading .removeClass('ipsHide') .find('.ipsLoading') .removeClass('ipsLoading_noAnim'); ips.utils.anim.go( 'fadeIn', commentLoading ); }, /** * Model event: something is done loading * * @param {event} e Event object * @param {object} data Event data object * @returns {void} */ commentDone: function (e, data) { if( data.commentID != this._commentID ){ return; } this.scope .find('[data-role="commentLoading"]') .addClass('ipsHide') .find('.ipsLoading') .addClass('ipsLoading_noAnim'); }, /** * Model event: edit form has loaded * * @param {event} e Event object * @param {object} data Event data object * @returns {void} */ getEditFormDone: function (e, data) { if( data.commentID != this._commentID ){ return; } var self = this; var showForm = _.once( function () { self._isEditing = true; self.scope.find('[data-action="expandTruncate"], [data-role="commentControls"]').hide(); self.scope.find('[data-action="editComment"]').parent('li').hide(); self.scope.find('[data-role="commentContent"]').html( data.response ); $( document ).trigger( 'contentChange', [ self.scope.find('[data-role="commentContent"]') ] ); }); // Scroll to the comment var elemPosition = ips.utils.position.getElemPosition( this.scope ); var windowScroll = $( window ).scrollTop(); var viewHeight = $( window ).height(); // Only scroll if it isn't already on the screen if( elemPosition.absPos.top < windowScroll || elemPosition.absPos.top > ( windowScroll + viewHeight ) ){ $('html, body').animate( { scrollTop: elemPosition.absPos.top + 'px' }, function () { showForm(); }); } else { showForm(); } }, /** * Model event: error loading edit form * * @param {event} e Event object * @param {object} data Event data object * @returns {void} */ getEditFormError: function (e, data) { if( data.commentID != this._commentID ){ return; } window.location = data.url; }, /** * Model event: saving an edit is finished * * @param {event} e Event object * @param {object} data Event data object * @returns {void} */ saveEditCommentDone: function (e, data) { if( data.commentID != this._commentID ){ return; } ips.ui.editor.destruct( this.scope.find('[data-ipseditor]') ); this._isEditing = false; this.scope.find('[data-role="commentContent"]').replaceWith( $('
' + $( this ).attr('data-quote-value') + '
' ); }); return html; }, /** * Parses the JSON object containing quote data for the comment * * @returns {object} Quote data, or else an empty object */ _getQuoteData: function () { if( !this._quoteData ){ try { this._quoteData = $.parseJSON( this.scope.attr('data-quoteData') ); return this._quoteData; } catch (err) { Debug.log("Couldn't parse quote data"); return {}; } } return this._quoteData; } }); }(jQuery, _)); ]]>
* {
* classes: {
* 'body': {
* 'background-color': 'red'
* },
* '.ipsButton': {
* 'color': 'blue'
* }
* }
* }
*
* @param {object} data Object of style data
* @returns {void}
*/
createStylesheet: function (data) {
this._custom.html( data.stylesheet || '' );
},
/**
* Starts the xray
*
* @returns {void}
*/
xrayStart: function () {
this._createXRayElem();
this._events['down'] = $( document ).on( 'mousedown.xray', _.bind( this._doDown, this ) );
this._events['move'] = $( document ).on( 'mousemove.xray', _.bind( this._doFalse, this ) );
this._events['over'] = $( document ).on( 'mouseover.xray', _.bind( this._doOver, this ) );
},
/**
* Cancels the xray
*
* @returns {void}
*/
xrayCancel: function () {
if( this._xrayElem && this._xrayElem.length ){
this._xrayElem.remove();
}
this._stop();
},
/**
* Stops the xray
*
* @returns {void}
*/
_stop: function () {
$( document ).off('.xray');
},
/**
* Initializes the xray function by resizing/positioning the xray element to fit the currently-hovered element
*
* @param {event} e Event object
* @returns {void}
*/
_doXray: function (e) {
e.preventDefault();
var elem = $( e.target );
if( elem.is( this._exclude ) ){
return;
}
var elemPosition = ips.utils.position.getElemPosition( elem );
var elemDims = { width: elem.outerWidth(), height: elem.outerHeight() };
this._xrayElem.css({
width: elemDims.width + 'px',
height: elemDims.height + 'px',
left: elemPosition.absPos.left + 'px',
top: elemPosition.absPos.top + 'px',
zIndex: ips.ui.zIndex()
});
},
/**
* Event handler for mousedown
*
* @param {event} e Event object
* @returns {void}
*/
_doDown: function (e) {
e.preventDefault();
this._doXray(e);
this._stop();
this._findMatchingSelectors( $( e.target ) );
},
/**
* Event handler for mouseover
* Highlight the hovered element
*
* @param {event} e Event object
* @returns {void}
*/
_doOver: function (e) {
this._doXray(e);
},
/**
* Called to prevent xray events from bubbling
*
* @param {event} e Event object
* @returns {void}
*/
_doFalse: function (e) {
e.preventDefault();
},
/**
* Creates the xray element and attaches it to body ready for this._doXray
*
* @param {event} e Event object
* @returns {void}
*/
_createXRayElem: function () {
if( $('#vseXRay').length ){
$('#vseXRay').remove();
}
$('body').append( $('').attr( 'id', 'vseXRay' ) );
this._xrayElem = $('#vseXRay');
},
/**
* Finds the ancestors of the clicked elemented, and matches selectors from our list
* Sends command to the main controller with our results
*
* @param {element} elem Clicked element
* @returns {void}
*/
_findMatchingSelectors: function (elem) {
var self = this;
var toReturn = [];
var loopSheets = function (thisElem) {
var rules = self._cssRules( thisElem );
// If we got rules, loop through them and see if we find any vars
if( rules.length ){
for( var i = 0; i < rules.length; i++ ){
var matches = rules[ i ].match(/\-\-theme\-(\w+)/i);
if( matches && matches.length ){
toReturn.push( matches[1] );
}
}
}
if( thisElem.parentNode ){
loopSheets( thisElem.parentNode );
}
return;
};
loopSheets( elem.get(0) );
this.sendCommand( 'varsMatched', {
vars: toReturn
});
},
/**
* Fetches all the css rules applying to the given element (but not inherited rules)
*
* @param {element} el Element
* @returns {array}
*/
_cssRules(el) {
var sheets = document.styleSheets, ret = [];
// Skip this check for the root document
if( el === document ){
return ret;
}
el.matches = el.matches || el.webkitMatchesSelector || el.mozMatchesSelector
|| el.msMatchesSelector || el.oMatchesSelector;
for (var i in sheets) {
try {
var rules = sheets[i].rules || sheets[i].cssRules;
for (var r in rules) {
// Safari throws an error if it sees a ::-webkit pseudo selector,
// so make sure this rule doesn't contain one.
var rule = this._removeVendorPrefixSelectors(rules[r].selectorText);
if (el.matches(rule)) {
console.log(`selector text: ${rules[r].selectorText}`)
ret.push(rules[r].cssText);
}
}
} catch (err) {
// Silently ignore errors here.
// Errors can be thrown if external stylesheets are checked (no permission to read rules)
// or other edge cases.
}
}
return ret;
},
/**
* Ignore rules that have vendor-prefixed pseudo selectors.
* @see https://stackoverflow.com/questions/45723466/vendor-prefix-pseudo-selector-crashes-element-matches-web-api-in-safari
*
* @param {element} el Element
* @returns {array}
*/
_removeVendorPrefixSelectors (selectorText) {
if (/::-/.test(selectorText)) {
//do nothing
} else {
return selectorText;
}
}
});
}(jQuery, _));]]>=h&&(c+=2);f>=k&&(w+=2)}}finally{g&&(g.style.display=a)}}catch(y){D.console&&console.log(y&&y.stack||y)}}var D="undefined"!==typeof window? window:{},B=["break,continue,do,else,for,if,return,while"],F=[[B,"auto,case,char,const,default,double,enum,extern,float,goto,inline,int,long,register,restrict,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"],"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],H=[F,"alignas,alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,delegate,dynamic_cast,explicit,export,friend,generic,late_check,mutable,namespace,noexcept,noreturn,nullptr,property,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"], O=[F,"abstract,assert,boolean,byte,extends,finally,final,implements,import,instanceof,interface,null,native,package,strictfp,super,synchronized,throws,transient"],P=[F,"abstract,add,alias,as,ascending,async,await,base,bool,by,byte,checked,decimal,delegate,descending,dynamic,event,finally,fixed,foreach,from,get,global,group,implicit,in,interface,internal,into,is,join,let,lock,null,object,out,override,orderby,params,partial,readonly,ref,remove,sbyte,sealed,select,set,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,value,var,virtual,where,yield"], F=[F,"abstract,async,await,constructor,debugger,enum,eval,export,function,get,import,implements,instanceof,interface,let,null,of,set,undefined,var,with,yield,Infinity,NaN"],Q=[B,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"],R=[B,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"], B=[B,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],S=/^(DIR|FILE|array|vector|(de|priority_)?queue|(forward_)?list|stack|(const_)?(reverse_)?iterator|(unordered_)?(multi)?(set|map)|bitset|u?(int|float)\d*)\b/,W=/\S/,X=x({keywords:[H,P,O,F,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",Q,R,B],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}), I={};t(X,["default-code"]);t(G([],[["pln",/^[^]+/],["dec",/^]*(?:>|$)/],["com",/^<\!--[\s\S]*?(?:-\->|$)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",/^