177 lines
5.9 KiB
JavaScript
Executable File
177 lines
5.9 KiB
JavaScript
Executable File
|
|
//Target the first letter of the first element found in the wrapper
|
|
(function ( $ ) {
|
|
//defaults
|
|
var pluginName = 'extLinks',
|
|
defaults = {
|
|
addIcon : true,
|
|
iconClassName : 'tc-external',
|
|
newTab: true,
|
|
skipSelectors : { //defines the selector to skip when parsing the wrapper
|
|
classes : [],
|
|
ids : []
|
|
},
|
|
skipChildTags : ['IMG']//skip those tags if they are direct children of the current link element
|
|
};
|
|
|
|
|
|
function Plugin( element, options ) {
|
|
this.$_el = $(element);
|
|
this.options = $.extend( {}, defaults, options) ;
|
|
this._href = ( 'string' == typeof( this.$_el.attr( 'href' ) ) ) ? this.$_el.attr( 'href' ).trim() : '';
|
|
this.init();
|
|
}
|
|
|
|
|
|
Plugin.prototype.init = function() {
|
|
var self = this,
|
|
$_external_icon = this.$_el.next( '.' + self.options.iconClassName );
|
|
//if not eligible, then remove any remaining icon element and return
|
|
//important => the element to remove is right after the current link element ( => use of '+' CSS operator )
|
|
if ( ! this._is_eligible() ) {
|
|
if ( $_external_icon.length )
|
|
$_external_icon.remove();
|
|
return;
|
|
}
|
|
//add the icon link, if not already there
|
|
if ( this.options.addIcon && 0 === $_external_icon.length ) {
|
|
this.$_el.append('<span class="' + self.options.iconClassName + '">');
|
|
}
|
|
|
|
//add the target _blank, if not already there
|
|
if ( this.options.newTab && '_blank' != this.$_el.attr('target') )
|
|
this.$_el.attr('target' , '_blank');
|
|
};
|
|
|
|
|
|
/*
|
|
* @return boolean
|
|
*/
|
|
Plugin.prototype._is_eligible = function() {
|
|
var self = this;
|
|
if ( ! this._is_external( this._href ) )
|
|
return;
|
|
|
|
//is first child tag allowed ?
|
|
if ( ! this._is_first_child_tag_allowed () )
|
|
return;
|
|
|
|
//are ids and classes selectors allowed ?
|
|
//all type of selectors (in the array) must pass the filter test
|
|
if ( 2 != ( ['ids', 'classes'].filter( function( sel_type) { return self._is_selector_allowed(sel_type); } ) ).length )
|
|
return;
|
|
|
|
var _is_eligible = true;
|
|
// disallow elements whose parent has text-decoration: underline
|
|
// we want to exit as soon as we find a parent with the underlined text-decoration
|
|
$.each( this.$_el.parents(), function() {
|
|
if ( 'underline' == $(this).css('textDecoration') ){
|
|
_is_eligible = false;
|
|
return false;
|
|
}
|
|
});
|
|
|
|
return true && _is_eligible;
|
|
};
|
|
|
|
|
|
/********
|
|
* HELPERS
|
|
*********/
|
|
/*
|
|
* @params string : ids or classes
|
|
* @return boolean
|
|
*/
|
|
Plugin.prototype._is_selector_allowed = function( requested_sel_type ) {
|
|
if ( czrapp && czrapp.userXP && czrapp.userXP.isSelectorAllowed )
|
|
return czrapp.userXP.isSelectorAllowed( this.$_el, this.options.skipSelectors, requested_sel_type);
|
|
|
|
var sel_type = 'ids' == requested_sel_type ? 'id' : 'class',
|
|
_selsToSkip = this.options.skipSelectors[requested_sel_type];
|
|
|
|
//check if option is well formed
|
|
if ( 'object' != typeof(this.options.skipSelectors) || ! this.options.skipSelectors[requested_sel_type] || ! Array.isArray( this.options.skipSelectors[requested_sel_type] ) || 0 === this.options.skipSelectors[requested_sel_type].length )
|
|
return true;
|
|
|
|
//has a forbidden parent?
|
|
if ( this.$_el.parents( _selsToSkip.map( function( _sel ){ return 'id' == sel_type ? '#' + _sel : '.' + _sel; } ).join(',') ).length > 0 )
|
|
return false;
|
|
|
|
//has requested sel ?
|
|
if ( ! this.$_el.attr( sel_type ) )
|
|
return true;
|
|
|
|
var _elSels = this.$_el.attr( sel_type ).split(' '),
|
|
_filtered = _elSels.filter( function(classe) { return -1 != $.inArray( classe , _selsToSkip ) ;});
|
|
|
|
//check if the filtered selectors array with the non authorized selectors is empty or not
|
|
//if empty => all selectors are allowed
|
|
//if not, at least one is not allowed
|
|
return 0 === _filtered.length;
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
* @return boolean
|
|
*/
|
|
Plugin.prototype._is_first_child_tag_allowed = function() {
|
|
//has children ?
|
|
if ( 0 === this.$_el.children().length )
|
|
return true;
|
|
|
|
var tagName = this.$_el.children().first()[0].tagName,
|
|
_tagToSkip = this.options.skipChildTags;
|
|
|
|
//check if tag to skip option is an array
|
|
if ( ! Array.isArray( _tagToSkip ) )
|
|
return true;
|
|
|
|
//make sure tags in option are all in uppercase
|
|
_tagToSkip = _tagToSkip.map( function( _tag ) { return _tag.toUpperCase(); });
|
|
return -1 == $.inArray( tagName , _tagToSkip );
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
* @return boolean
|
|
*/
|
|
Plugin.prototype._is_external = function( _href ) {
|
|
//gets main domain and extension, no matter if it is a n level sub domain
|
|
//works also with localhost or numeric urls
|
|
var _main_domain = (location.host).split('.').slice(-2).join('.'),
|
|
_reg = new RegExp( _main_domain );
|
|
|
|
if ( 'string' != typeof( _href ) )
|
|
return;
|
|
|
|
_href = _href.trim();
|
|
|
|
if ( _href !== '' && _href != '#' && this._isValidURL( _href ) )
|
|
return ! _reg.test( _href );
|
|
return;
|
|
};
|
|
|
|
|
|
/*
|
|
* @return boolean
|
|
*/
|
|
Plugin.prototype._isValidURL = function( _url ){
|
|
var _pattern = /(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/;
|
|
return _pattern.test( _url );
|
|
};
|
|
|
|
|
|
// prevents against multiple instantiations
|
|
$.fn[pluginName] = function ( options ) {
|
|
return this.each(function () {
|
|
if (!$.data(this, 'plugin_' + pluginName)) {
|
|
$.data(this, 'plugin_' + pluginName,
|
|
new Plugin( this, options ));
|
|
}
|
|
});
|
|
};
|
|
|
|
})( jQuery );
|