text:
/*
Name: 			Theme Base
Written by: 	Okler Themes - (http://www.okler.net)
Theme Version: 	1.4.1
*/

window.theme = {};

// Navigation
(function( $ ) {

	'use strict';

	var $items = $( '.nav-main li.nav-parent' );

	function expand( $li ) {
		$li.children( 'ul.nav-children' ).slideDown( 'fast', function() {
			$li.addClass( 'nav-expanded' );
			$(this).css( 'display', '' );
			ensureVisible( $li );
		});
	}

	function collapse( $li ) {
		$li.children('ul.nav-children' ).slideUp( 'fast', function() {
			$(this).css( 'display', '' );
			$li.removeClass( 'nav-expanded' );
		});
	}

	function ensureVisible( $li ) {
		var scroller = $li.offsetParent();
		if ( !scroller.get(0) ) {
			return false;
		}

		var top = $li.position().top;
		if ( top < 0 ) {
			scroller.animate({
				scrollTop: scroller.scrollTop() + top
			}, 'fast');
		}
	}

	$items.find('> a').on('click', function( ev ) {

		var $anchor = $( this ),
			$prev = $anchor.closest('ul.nav').find('> li.nav-expanded' ),
			$next = $anchor.closest('li');

		if ( $anchor.prop('href') ) {
			var arrowWidth = parseInt(window.getComputedStyle($anchor.get(0), ':after').width, 10) || 0;
			if (ev.offsetX > $anchor.get(0).offsetWidth - arrowWidth) {
				ev.preventDefault();
			}
		}

		if ( $prev.get( 0 ) !== $next.get( 0 ) ) {
			collapse( $prev );
			expand( $next );
		} else {
			collapse( $prev );
		}
	});

}).apply( this, [ jQuery ]);

// Skeleton
(function(theme, $) {

	'use strict';

	theme = theme || {};

	var $body		= $( 'body' ),
		$html		= $( 'html' ),
		$window		= $( window ),
		isAndroid	= navigator.userAgent.toLowerCase().indexOf('android') > -1;

	// mobile devices with fixed has a lot of issues when focus inputs and others...
	if ( typeof $.browser !== 'undefined' && $.browser.mobile && $html.hasClass('fixed') ) {
		$html.removeClass( 'fixed' ).addClass( 'scroll' );
	}

	var Skeleton = {

		options: {
			sidebars: {
				menu: '#content-menu',
				left: '#sidebar-left',
				right: '#sidebar-right'
			}
		},

		customScroll: ( !Modernizr.overflowscrolling && !isAndroid && $.fn.nanoScroller !== 'undefined'),

		initialize: function() {
			this
				.setVars()
				.build()
				.events();
		},

		setVars: function() {
			this.sidebars = {};

			this.sidebars.left = {
				$el: $( this.options.sidebars.left )
			};

			this.sidebars.right = {
				$el: $( this.options.sidebars.right ),
				isOpened: $html.hasClass( 'sidebar-right-opened' )
			};

			this.sidebars.menu = {
				$el: $( this.options.sidebars.menu ),
				isOpened: $html.hasClass( 'inner-menu-opened' )
			};

			return this;
		},

		build: function() {

			if ( typeof $.browser !== 'undefined' && $.browser.mobile ) {
				$html.addClass( 'mobile-device' );
			} else {
				$html.addClass( 'no-mobile-device' );
			}

			$html.addClass( 'custom-scroll' );
			if ( this.customScroll ) {
				this.buildSidebarLeft();
				this.buildContentMenu();
			}

			this.buildSidebarRight();

			return this;
		},

		events: function() {
			if ( this.customScroll ) {
				this.eventsSidebarLeft();
			}

			this.eventsSidebarRight();
			this.eventsContentMenu();

			if ( typeof $.browser !== 'undefined' && !this.customScroll && isAndroid ) {
				this.fixScroll();
			}

			return this;
		},

		fixScroll: function() {
			var _self = this;

			$window
				.on( 'sidebar-left-opened sidebar-right-toggle', function( e, data ) {
					_self.preventBodyScrollToggle( data.added );
				});
		},

		buildSidebarLeft: function() {
			this.sidebars.left.isOpened = !$html.hasClass( 'sidebar-left-collapsed' ) || $html.hasClass( 'sidebar-left-opened' );

			this.sidebars.left.$nano = this.sidebars.left.$el.find( '.nano' );

			this.sidebars.left.$nano.nanoScroller({
				alwaysVisible: true,
				preventPageScrolling: true
			});

			return this;
		},

		eventsSidebarLeft: function() {

			var _self = this,
				$nano = this.sidebars.left.$nano;

			var open = function() {
				if ( _self.sidebars.left.isOpened ) {
					return close();
				}

				_self.sidebars.left.isOpened = true;

				$html.addClass( 'sidebar-left-opened' );

				$window.trigger( 'sidebar-left-toggle', {
					added: true,
					removed: false
				});

				$html.on( 'click.close-left-sidebar', function(e) {
					e.stopPropagation();
					close(e);
				});

			};

			var close = function(e) {
				if ( !!e && !!e.target && ($(e.target).closest( '.sidebar-left' ).get(0) || !$(e.target).closest( 'html' ).get(0)) ) {
					e.preventDefault();
					return false;
				} else {

					$html.removeClass( 'sidebar-left-opened' );
					$html.off( 'click.close-left-sidebar' );

					$window.trigger( 'sidebar-left-toggle', {
						added: false,
						removed: true
					});

					_self.sidebars.left.isOpened = !$html.hasClass( 'sidebar-left-collapsed' );

				}
			};

			var updateNanoScroll = function() {
				if ( $.support.transition ) {
					$nano.nanoScroller();
					$nano
						.one('bsTransitionEnd', updateNanoScroll)
						.emulateTransitionEnd(150)
				} else {
					updateNanoScroll();
				}
			};

			var isToggler = function( element ) {
				return $(element).data('fire-event') === 'sidebar-left-toggle' || $(element).parents().data('fire-event') === 'sidebar-left-toggle';
			};

			this.sidebars.left.$el
				.on( 'click', function() {
					updateNanoScroll();
				})
				.on('touchend', function(e) {
					_self.sidebars.left.isOpened = !$html.hasClass( 'sidebar-left-collapsed' ) || $html.hasClass( 'sidebar-left-opened' );
					if ( !_self.sidebars.left.isOpened && !isToggler(e.target) ) {
						e.stopPropagation();
						e.preventDefault();
						open();
					}
				});

			$nano
				.on( 'mouseenter', function() {
					if ( $html.hasClass( 'sidebar-left-collapsed' ) ) {
						$nano.nanoScroller();
					}
				})
				.on( 'mouseleave', function() {
					if ( $html.hasClass( 'sidebar-left-collapsed' ) ) {
						$nano.nanoScroller();
					}
				});

			$window.on( 'sidebar-left-toggle', function(e, toggle) {
				if ( toggle.removed ) {
					$html.removeClass( 'sidebar-left-opened' );
					$html.off( 'click.close-left-sidebar' );
				}
			});

			return this;
		},

		buildSidebarRight: function() {
			this.sidebars.right.isOpened = $html.hasClass( 'sidebar-right-opened' );

			if ( this.customScroll ) {
				this.sidebars.right.$nano = this.sidebars.right.$el.find( '.nano' );

				this.sidebars.right.$nano.nanoScroller({
					alwaysVisible: true,
					preventPageScrolling: true
				});
			}

			return this;
		},

		eventsSidebarRight: function() {
			var _self = this;

			var open = function() {
				if ( _self.sidebars.right.isOpened ) {
					return close();
				}

				_self.sidebars.right.isOpened = true;

				$html.addClass( 'sidebar-right-opened' );

				$window.trigger( 'sidebar-right-toggle', {
					added: true,
					removed: false
				});

				$html.on( 'click.close-right-sidebar', function(e) {
					e.stopPropagation();
					close(e);
				});
			};

			var close = function(e) {
				if ( !!e && !!e.target && ($(e.target).closest( '.sidebar-right' ).get(0) || !$(e.target).closest( 'html' ).get(0)) ) {
					e.preventDefault();
					return false;
				}

				$html.removeClass( 'sidebar-right-opened' );
				$html.off( 'click.close-right-sidebar' );

				$window.trigger( 'sidebar-right-toggle', {
					added: false,
					removed: true
				});

				_self.sidebars.right.isOpened = false;
			};

			var bind = function() {
				$('[data-open="sidebar-right"]').on('click', function(e) {
					var $el = $(this);
					e.stopPropagation();

					if ( $el.is('a') )
						e.preventDefault();

					open();
				});
			};

			this.sidebars.right.$el.find( '.mobile-close' )
				.on( 'click', function( e ) {
					e.preventDefault();
					$html.trigger( 'click.close-right-sidebar' );
				});

			bind();

			return this;
		},

		buildContentMenu: function() {
			if ( !$html.hasClass( 'fixed' ) ) {
				return false;
			}

			this.sidebars.menu.$nano = this.sidebars.menu.$el.find( '.nano' );

			this.sidebars.menu.$nano.nanoScroller({
				alwaysVisible: true,
				preventPageScrolling: true
			});

			return this;
		},

		eventsContentMenu: function() {
			var _self = this;

			var open = function() {
				if ( _self.sidebars.menu.isOpened ) {
					return close();
				}

				_self.sidebars.menu.isOpened = true;

				$html.addClass( 'inner-menu-opened' );

				$window.trigger( 'inner-menu-toggle', {
					added: true,
					removed: false
				});

				$html.on( 'click.close-inner-menu', function(e) {

					close(e);
				});

			};

			var close = function(e) {
				if ( !!e && !!e.target && !$(e.target).closest( '.inner-menu-collapse' ).get(0) && ($(e.target).closest( '.inner-menu' ).get(0) || !$(e.target).closest( 'html' ).get(0)) ) {
					return false;
				}

				e.stopPropagation();

				$html.removeClass( 'inner-menu-opened' );
				$html.off( 'click.close-inner-menu' );

				$window.trigger( 'inner-menu-toggle', {
					added: false,
					removed: true
				});

				_self.sidebars.menu.isOpened = false;
			};

			var bind = function() {
				$('[data-open="inner-menu"]').on('click', function(e) {
					var $el = $(this);
					e.stopPropagation();

					if ( $el.is('a') )
						e.preventDefault();

					open();
				});
			};

			bind();

			/* Nano Scroll */
			if ( $html.hasClass( 'fixed' ) ) {
				var $nano = this.sidebars.menu.$nano;

				var updateNanoScroll = function() {
					if ( $.support.transition ) {
						$nano.nanoScroller();
						$nano
							.one('bsTransitionEnd', updateNanoScroll)
							.emulateTransitionEnd(150)
					} else {
						updateNanoScroll();
					}
				};

				this.sidebars.menu.$el
					.on( 'click', function() {
						updateNanoScroll();
					});
			}

			return this;
		},

		preventBodyScrollToggle: function( shouldPrevent, $el ) {
			setTimeout(function() {
				if ( shouldPrevent ) {
					$body
						.data( 'scrollTop', $body.get(0).scrollTop )
						.css({
							position: 'fixed',
							top: $body.get(0).scrollTop * -1
						})
				} else {
					$body
						.css({
							position: '',
							top: ''
						})
						.scrollTop( $body.data( 'scrollTop' ) );
				}
			}, 150);
		}

	};

	// expose to scope
	$.extend(theme, {
		Skeleton: Skeleton
	});

}).apply(this, [ window.theme, jQuery ]);

// Base
(function(theme, $) {

	'use strict';

	theme = theme || {};

	theme.Skeleton.initialize();

}).apply(this, [ window.theme, jQuery ]);

/*
Name: 			Theme Admin Extension
Written by: 	Okler Themes - (http://www.okler.net)
Theme Version: 	1.4.1
*/

window.theme = {};

// Mailbox
(function(theme, $) {

	theme = theme || {};

	var instanceName = '__mailbox';

	var capitalizeString = function( str ) {
    	return str.charAt( 0 ).toUpperCase() + str.slice( 1 );
	}

	var Mailbox = function($wrapper) {
		return this.initialize($wrapper);
	};

	Mailbox.prototype = {
		initialize: function($wrapper) {
			if ( $wrapper.data( instanceName ) ) {
				return this;
			}

			this.$wrapper = $wrapper;

			this
				.setVars()
				.setData()
				.build()
				.events();

			return this;
		},

		setVars: function() {
			this.view = capitalizeString( this.$wrapper.data( 'mailbox-view' ) || "" );

			return this;
		},

		setData: function() {
			this.$wrapper.data(instanceName, this);

			return this;
		},

		build: function() {

			if ( typeof this[ 'build' + this.view ] === 'function' ) {
				this[ 'build' + this.view ].call( this );
			}

			return this;
		},

		events: function() {
			if ( typeof this[ 'events' + this.view ] === 'function' ) {
				this[ 'events' + this.view ].call( this );
			}

			return this;
		},

		buildFolder: function() {
			this.$wrapper.find('.mailbox-email-list .nano').nanoScroller({
				alwaysVisible: true,
				preventPageScrolling: true
			});
		},

		buildEmail: function() {
			this.buildComposer();
		},

		buildCompose: function() {
			this.buildComposer();
		},

		buildComposer: function() {
			this.$wrapper.find( '#compose-field' ).summernote({
				height: 250,
				toolbar: [
					['style', ['style']],
					['font', ['bold', 'italic', 'underline', 'clear']],
					['fontname', ['fontname']],
					['color', ['color']],
					['para', ['ul', 'ol', 'paragraph']],
					['height', ['height']],
					['table', ['table']],
					['insert', ['link', 'picture', 'video']],
					['view', ['fullscreen']],
					['help', ['help']]
				]
			});
		},

		eventsCompose: function() {
			var $composer,
				$contentBody,
				$html,
				$innerBody;

			$composer		= $( '.note-editable' );
			$contentBody	= $( '.content-body' );
			$html			= $( 'html' );
			$innerBody		= $( '.inner-body' );

			var adjustComposeSize = function() {
				var composerHeight,
					composerTop,
					contentBodyPaddingBottom,
					innerBodyHeight,
					viewportHeight,
					viewportWidth;

				contentBodyPaddingBottom	= parseInt( $contentBody.css('paddingBottom'), 10 ) || 0;
				viewportHeight				= Math.max( document.documentElement.clientHeight, window.innerHeight || 0 );
				viewportWidth				= Math.max( document.documentElement.clientWidth, window.innerWidth || 0 );

				$composer.css( 'height', '' );

				if ( viewportWidth < 767 || $html.hasClass('mobile-device') ) {
					composerTop	   = $composer.offset().top;
					composerHeight = viewportHeight - composerTop;
				} else {
					if ( $html.hasClass( 'fixed' ) ) {
						composerTop	= $composer.offset().top;
					} else {
						composerTop	= $composer.position().top;
					}
					composerHeight = $innerBody.outerHeight() - composerTop;
				}

				composerHeight -= contentBodyPaddingBottom;

				$composer.css({
					height: composerHeight
				});
			};

			var timer;
			$(window)
				.on( 'resize orientationchange sidebar-left-toggle mailbox-recalc', function() {
					clearTimeout( timer );
					timer = setTimeout(function() {
						adjustComposeSize();
					}, 100);
				});

			adjustComposeSize();
		}
	};

	// expose to scope
	$.extend(theme, {
		Mailbox: Mailbox
	});

	// jquery plugin
	$.fn.themeMailbox = function(opts) {
		return this.each(function() {
			var $this = $(this);

			if ($this.data(instanceName)) {
				return $this.data(instanceName);
			} else {
				return new Mailbox($this);
			}

		});
	}

}).apply(this, [ window.theme, jQuery ]);

// Lock Screen
(function( $ ) {

	'use strict';

	var LockScreen = {

		initialize: function() {
			this.$body = $( 'body' );

			this
				.build()
				.events();
		},

		build: function() {
			var lockHTML,
				userinfo;

			userinfo = this.getUserInfo();
			this.lockHTML = this.buildTemplate( userinfo );

			this.$lock        = this.$body.children( '#LockScreenInline' );
			this.$userPicture = this.$lock.find( '#LockUserPicture' );
			this.$userName    = this.$lock.find( '#LockUserName' );
			this.$userEmail   = this.$lock.find( '#LockUserEmail' );

			return this;
		},

		events: function() {
			var _self = this;

			this.$body.find( '[data-lock-screen="true"]' ).on( 'click', function( e ) {
				e.preventDefault();

				_self.show();
			});

			return this;
		},

		formEvents: function( $form ) {
			var _self = this;

			$form.on( 'submit', function( e ) {
				e.preventDefault();

				_self.hide();
			});
		},

		show: function() {
			var _self = this,
				userinfo = this.getUserInfo();

			this.$userPicture.attr( 'src', userinfo.picture );
			this.$userName.text( userinfo.username );
			this.$userEmail.text( userinfo.email );

			this.$body.addClass( 'show-lock-screen' );

			$.magnificPopup.open({
				items: {
					src: this.lockHTML,
					type: 'inline'
				},
				modal: true,
				mainClass: 'mfp-lock-screen',
				callbacks: {
					change: function() {
						_self.formEvents( this.content.find( 'form' ) );
					}
				}
			});
		},

		hide: function() {
			$.magnificPopup.close();
		},

		getUserInfo: function() {
			var $info,
				picture,
				name,
				email;

			// always search in case something is changed through ajax
			$info    = $( '#userbox' );
			picture  = $info.find( '.profile-picture img' ).attr( 'data-lock-picture' );
			name     = $info.find( '.profile-info' ).attr( 'data-lock-name' );
			email    = $info.find( '.profile-info' ).attr( 'data-lock-email' );

			return {
				picture: picture,
				username: name,
				email: email
			};
		},

		buildTemplate: function( userinfo ) {
			return [
					'
',
						'
',
							'
',
								'
',
									'<form>',
										'
',
											'',
											'
{{username}}
',
											'
{{email}}
',
										'
',
										'
',
											'
',
												'<input id="pwd" name="pwd" type="password" class="form-control input-lg" placeholder="Password" />',
												'',
													'',
														'',
													'',
												'',
											'
',
										'
',

										'
',
											'
',
												'
',
													'Not John Doe?',
												'
',
											'
',
											'
',
												'<button type="submit" class="btn btn-primary">Unlock</button>',
											'
',
										'
',
									'</form>',
								'
',
							'
',
						'
',
					'
'
				]
				.join( '' )
				.replace( /\{\{picture\}\}/, userinfo.picture )
				.replace( /\{\{username\}\}/, userinfo.username )
				.replace( /\{\{email\}\}/, userinfo.email );
		}

	};

	this.LockScreen = LockScreen;

	$(function() {
		LockScreen.initialize();
	});

}).apply(this, [ jQuery ]);

// Panels
(function( $ ) {

	$(function() {
		$('.panel')
			.on( 'panel:toggle', function() {
				var $this,
					direction;

				$this = $(this);
				direction = $this.hasClass( 'panel-collapsed' ) ? 'Down' : 'Up';

				$this.find('.panel-body, .panel-footer')[ 'slide' + direction ]( 200, function() {
					$this[ (direction === 'Up' ? 'add' : 'remove') + 'Class' ]( 'panel-collapsed' )
				});
			})
			.on( 'panel:dismiss', function() {
				var $this = $(this);

				if ( !!( $this.parent('div').attr('class') || '' ).match( /col-(xs|sm|md|lg)/g ) && $this.siblings().length === 0 ) {
					$row = $this.closest('.row');
					$this.parent('div').remove();
					if ( $row.children().length === 0 ) {
						$row.remove();
					}
				} else {
					$this.remove();
				}
			})
			.on( 'click', '[data-panel-toggle]', function( e ) {
				e.preventDefault();
				$(this).closest('.panel').trigger( 'panel:toggle' );
			})
			.on( 'click', '[data-panel-dismiss]', function( e ) {
				e.preventDefault();
				$(this).closest('.panel').trigger( 'panel:dismiss' );
			})
			/* Deprecated */
			.on( 'click', '.panel-actions a.fa-caret-up', function( e ) {
				e.preventDefault();
				var $this = $( this );

				$this
					.removeClass( 'fa-caret-up' )
					.addClass( 'fa-caret-down' );

				$this.closest('.panel').trigger( 'panel:toggle' );
			})
			.on( 'click', '.panel-actions a.fa-caret-down', function( e ) {
				e.preventDefault();
				var $this = $( this );

				$this
					.removeClass( 'fa-caret-down' )
					.addClass( 'fa-caret-up' );

				$this.closest('.panel').trigger( 'panel:toggle' );
			})
			.on( 'click', '.panel-actions a.fa-times', function( e ) {
				e.preventDefault();
				var $this = $( this );

				$this.closest('.panel').trigger( 'panel:dismiss' );
			});
	});

})( jQuery );

// Bootstrap Toggle
(function( $ ) {

	'use strict';

	var $window = $( window );

	var toggleClass = function( $el ) {
		if ( !!$el.data('toggleClassBinded') ) {
			return false;
		}

		var $target,
			className,
			eventName;

		$target = $( $el.attr('data-target') );
		className = $el.attr('data-toggle-class');
		eventName = $el.attr('data-fire-event');

		$el.on('click.toggleClass', function(e) {
			e.preventDefault();
			$target.toggleClass( className );

			var hasClass = $target.hasClass( className );

			if ( !!eventName ) {
				$window.trigger( eventName, {
					added: hasClass,
					removed: !hasClass
				});
			}
		});

		$el.data('toggleClassBinded', true);

		return true;
	};

	$(function() {
		$('[data-toggle-class][data-target]').each(function() {
			toggleClass( $(this) );
		});
	});

}).apply( this, [ jQuery ]);

// Form to Object
(function( $ ) {

	'use strict';

	$.fn.formToObject = function() {
		var arrayData,
			objectData;

		arrayData	= this.serializeArray();
		objectData	= {};

		$.each( arrayData, function() {
			var value;

			if (this.value != null) {
				value = this.value;
			} else {
				value = '';
			}

			if (objectData[this.name] != null) {
				if (!objectData[this.name].push) {
					objectData[this.name] = [objectData[this.name]];
				}

				objectData[this.name].push(value);
			} else {
				objectData[this.name] = value;
			}
		});

		return objectData;
	};

})( jQuery );

// Loading Overlay
(function(theme, $) {

	'use strict';

	theme = theme || {};

	var loadingOverlayTemplate = [
		'
',
			'

',
		'
'
	].join('');

	var LoadingOverlay = function( $wrapper, options ) {
		return this.initialize( $wrapper, options );
	};

	LoadingOverlay.prototype = {

		options: {
			css: {}
		},

		initialize: function( $wrapper, options ) {
			this.$wrapper = $wrapper;

			this
				.setVars()
				.setOptions( options )
				.build()
				.events();

			this.$wrapper.data( 'loadingOverlay', this );
		},

		setVars: function() {
			this.$overlay = this.$wrapper.find('.loading-overlay');

			return this;
		},

		setOptions: function( options ) {
			if ( !this.$overlay.get(0) ) {
				this.matchProperties();
			}
			this.options     = $.extend( true, {}, this.options, options );
			this.loaderClass = this.getLoaderClass( this.options.css.backgroundColor );

			return this;
		},

		build: function() {
			if ( !this.$overlay.closest(document.documentElement).get(0) ) {
				if ( !this.$cachedOverlay ) {
					this.$overlay = $( loadingOverlayTemplate ).clone();

					if ( this.options.css ) {
						this.$overlay.css( this.options.css );
						this.$overlay.find( '.loader' ).addClass( this.loaderClass );
					}
				} else {
					this.$overlay = this.$cachedOverlay.clone();
				}

				this.$wrapper.append( this.$overlay );
			}

			if ( !this.$cachedOverlay ) {
				this.$cachedOverlay = this.$overlay.clone();
			}

			return this;
		},

		events: function() {
			var _self = this;

			if ( this.options.startShowing ) {
				_self.show();
			}

			if ( this.$wrapper.is('body') || this.options.hideOnWindowLoad ) {
				$( window ).on( 'load error', function() {
					_self.hide();
				});
			}

			if ( this.options.listenOn ) {
				$( this.options.listenOn )
					.on( 'loading-overlay:show beforeSend.ic', function( e ) {
						e.stopPropagation();
						_self.show();
					})
					.on( 'loading-overlay:hide complete.ic', function( e ) {
						e.stopPropagation();
						_self.hide();
					});
			}

			this.$wrapper
				.on( 'loading-overlay:show beforeSend.ic', function( e ) {
					e.stopPropagation();
					_self.show();
				})
				.on( 'loading-overlay:hide complete.ic', function( e ) {
					e.stopPropagation();
					_self.hide();
				});

			return this;
		},

		show: function() {
			this.build();

			this.position = this.$wrapper.css( 'position' ).toLowerCase();
			if ( this.position != 'relative' || this.position != 'absolute' || this.position != 'fixed' ) {
				this.$wrapper.css({
					position: 'relative'
				});
			}
			this.$wrapper.addClass( 'loading-overlay-showing' );
		},

		hide: function() {
			var _self = this;

			this.$wrapper.removeClass( 'loading-overlay-showing' );
			setTimeout(function() {
				if ( this.position != 'relative' || this.position != 'absolute' || this.position != 'fixed' ) {
					_self.$wrapper.css({ position: '' });
				}
			}, 500);
		},

		matchProperties: function() {
			var i,
				l,
				properties;

			properties = [
				'backgroundColor',
				'borderRadius'
			];

			l = properties.length;

			for( i = 0; i < l; i++ ) {
				var obj = {};
				obj[ properties[ i ] ] = this.$wrapper.css( properties[ i ] );

				$.extend( this.options.css, obj );
			}
		},

		getLoaderClass: function( backgroundColor ) {
			if ( !backgroundColor || backgroundColor === 'transparent' || backgroundColor === 'inherit' ) {
				return 'black';
			}

			var hexColor,
				r,
				g,
				b,
				yiq;

			var colorToHex = function( color ){
				var hex,
					rgb;

				if( color.indexOf('#') >- 1 ){
					hex = color.replace('#', '');
				} else {
					rgb = color.match(/\d+/g);
					hex = ('0' + parseInt(rgb[0], 10).toString(16)).slice(-2) + ('0' + parseInt(rgb[1], 10).toString(16)).slice(-2) + ('0' + parseInt(rgb[2], 10).toString(16)).slice(-2);
				}

				if ( hex.length === 3 ) {
					hex = hex + hex;
				}

				return hex;
			};

			hexColor = colorToHex( backgroundColor );

			r = parseInt( hexColor.substr( 0, 2), 16 );
			g = parseInt( hexColor.substr( 2, 2), 16 );
			b = parseInt( hexColor.substr( 4, 2), 16 );
			yiq = ((r * 299) + (g * 587) + (b * 114)) / 1000;

			return ( yiq >= 128 ) ? 'black' : 'white';
		}

	};

	// expose to scope
	$.extend(theme, {
		LoadingOverlay: LoadingOverlay
	});

	// expose as a jquery plugin
	$.fn.loadingOverlay = function( opts ) {
		return this.each(function() {
			var $this = $( this );

			var loadingOverlay = $this.data( 'loadingOverlay' );
			if ( loadingOverlay ) {
				return loadingOverlay;
			} else {
				var options = opts || $this.data( 'loading-overlay-options' ) || {};
				return new LoadingOverlay( $this, options );
			}
		});
	}

	// auto init
	$(function() {
		$('[data-loading-overlay]').loadingOverlay();
	});

}).apply(this, [window.theme, jQuery]);

// Scroll to Top
(function(theme, $) {

	theme = theme || {};

	$.extend(theme, {

		PluginScrollToTop: {

			defaults: {
				wrapper: $('body'),
				offset: 150,
				buttonClass: 'scroll-to-top',
				iconClass: 'fa fa-chevron-up',
				delay: 500,
				visibleMobile: false,
				label: false
			},

			initialize: function(opts) {
				initialized = true;

				this
					.setOptions(opts)
					.build()
					.events();

				return this;
			},

			setOptions: function(opts) {
				this.options = $.extend(true, {}, this.defaults, opts);

				return this;
			},

			build: function() {
				var self = this,
					$el;

				// Base HTML Markup
				$el = $('')
					.addClass(self.options.buttonClass)
					.attr({
						'href': '#',
					})
					.append(
						$('')
						.addClass(self.options.iconClass)
				);

				// Visible Mobile
				if (!self.options.visibleMobile) {
					$el.addClass('hidden-mobile');
				}

				// Label
				if (self.options.label) {
					$el.append(
						$('').html(self.options.label)
					);
				}

				this.options.wrapper.append($el);

				this.$el = $el;

				return this;
			},

			events: function() {
				var self = this,
					_isScrolling = false;

				// Click Element Action
				self.$el.on('click', function(e) {
					e.preventDefault();
					$('body, html').animate({
						scrollTop: 0
					}, self.options.delay);
					return false;
				});

				// Show/Hide Button on Window Scroll event.
				$(window).scroll(function() {

					if (!_isScrolling) {

						_isScrolling = true;

						if ($(window).scrollTop() > self.options.offset) {

							self.$el.stop(true, true).addClass('visible');
							_isScrolling = false;

						} else {

							self.$el.stop(true, true).removeClass('visible');
							_isScrolling = false;

						}

					}

				});

				return this;
			}

		}

	});

}).apply(this, [window.theme, jQuery]);

// Codemirror
(function(theme, $) {

	theme = theme || {};

	var instanceName = '__codemirror';

	var PluginCodeMirror = function($el, opts) {
		return this.initialize($el, opts);
	};

	PluginCodeMirror.defaults = {
		lineNumbers: true,
		styleActiveLine: true,
		matchBrackets: true,
		theme: 'monokai'
	};

	PluginCodeMirror.prototype = {
		initialize: function($el, opts) {
			if ( $el.data( instanceName ) ) {
				return this;
			}

			this.$el = $el;

			this
				.setData()
				.setOptions(opts)
				.build();

			return this;
		},

		setData: function() {
			this.$el.data(instanceName, this);

			return this;
		},

		setOptions: function(opts) {
			this.options = $.extend( true, {}, PluginCodeMirror.defaults, opts );

			return this;
		},

		build: function() {
			CodeMirror.fromTextArea( this.$el.get(0), this.options );

			return this;
		}
	};

	// expose to scope
	$.extend(theme, {
		PluginCodeMirror: PluginCodeMirror
	});

	// jquery plugin
	$.fn.themePluginCodeMirror = function(opts) {
		return this.each(function() {
			var $this = $(this);

			if ($this.data(instanceName)) {
				return $this.data(instanceName);
			} else {
				return new PluginCodeMirror($this, opts);
			}

		});
	}

}).apply(this, [ window.theme, jQuery ]);

// Colorpicker
(function(theme, $) {

	theme = theme || {};

	var instanceName = '__colorpicker';

	var PluginColorPicker = function($el, opts) {
		return this.initialize($el, opts);
	};

	PluginColorPicker.defaults = {
	};

	PluginColorPicker.prototype = {
		initialize: function($el, opts) {
			if ( $el.data( instanceName ) ) {
				return this;
			}

			this.$el = $el;

			this
				.setData()
				.setOptions(opts)
				.build();

			return this;
		},

		setData: function() {
			this.$el.data(instanceName, this);

			return this;
		},

		setOptions: function(opts) {
			this.options = $.extend( true, {}, PluginColorPicker.defaults, opts );

			return this;
		},

		build: function() {
			this.$el.colorpicker( this.options );

			return this;
		}
	};

	// expose to scope
	$.extend(theme, {
		PluginColorPicker: PluginColorPicker
	});

	// jquery plugin
	$.fn.themePluginColorPicker = function(opts) {
		return this.each(function() {
			var $this = $(this);

			if ($this.data(instanceName)) {
				return $this.data(instanceName);
			} else {
				return new PluginColorPicker($this, opts);
			}

		});
	}

}).apply(this, [ window.theme, jQuery ]);

// Datepicker
(function(theme, $) {

	theme = theme || {};

	var instanceName = '__datepicker';

	var PluginDatePicker = function($el, opts) {
		return this.initialize($el, opts);
	};

	PluginDatePicker.defaults = {
	};

	PluginDatePicker.prototype = {
		initialize: function($el, opts) {
			if ( $el.data( instanceName ) ) {
				return this;
			}

			this.$el = $el;

			this
				.setVars()
				.setData()
				.setOptions(opts)
				.build();

			return this;
		},

		setVars: function() {
			this.skin = this.$el.data( 'plugin-skin' );

			return this;
		},

		setData: function() {
			this.$el.data(instanceName, this);

			return this;
		},

		setOptions: function(opts) {
			this.options = $.extend( true, {}, PluginDatePicker.defaults, opts );

			return this;
		},

		build: function() {
			this.$el.datepicker( this.options );

			if ( !!this.skin ) {
				this.$el.data('datepicker').picker.addClass( 'datepicker-' + this.skin );
			}

			return this;
		}
	};

	// expose to scope
	$.extend(theme, {
		PluginDatePicker: PluginDatePicker
	});

	// jquery plugin
	$.fn.themePluginDatePicker = function(opts) {
		return this.each(function() {
			var $this = $(this);

			if ($this.data(instanceName)) {
				return $this.data(instanceName);
			} else {
				return new PluginDatePicker($this, opts);
			}

		});
	}

}).apply(this, [ window.theme, jQuery ]);

// iosSwitcher
(function(theme, $) {

	theme = theme || {};

	var instanceName = '__IOS7Switch';

	var PluginIOS7Switch = function($el) {
		return this.initialize($el);
	};

	PluginIOS7Switch.prototype = {
		initialize: function($el) {
			if ( $el.data( instanceName ) ) {
				return this;
			}

			this.$el = $el;

			this
				.setData()
				.build();

			return this;
		},

		setData: function() {
			this.$el.data(instanceName, this);

			return this;
		},

		build: function() {
			var switcher = new Switch( this.$el.get(0) );

			$( switcher.el ).on( 'click', function( e ) {
				e.preventDefault();
				switcher.toggle();
			});

			return this;
		}
	};

	// expose to scope
	$.extend(theme, {
		PluginIOS7Switch: PluginIOS7Switch
	});

	// jquery plugin
	$.fn.themePluginIOS7Switch = function(opts) {
		return this.each(function() {
			var $this = $(this);

			if ($this.data(instanceName)) {
				return $this.data(instanceName);
			} else {
				return new PluginIOS7Switch($this);
			}

		});
	}

}).apply(this, [ window.theme, jQuery ]);

// Markdown
(function(theme, $) {

	theme = theme || {};

	var instanceName = '__markdownEditor';

	var PluginMarkdownEditor = function($el, opts) {
		return this.initialize($el, opts);
	};

	PluginMarkdownEditor.defaults = {
		iconlibrary: 'fa'
	};

	PluginMarkdownEditor.prototype = {
		initialize: function($el, opts) {
			if ( $el.data( instanceName ) ) {
				return this;
			}

			this.$el = $el;

			this
				.setData()
				.setOptions(opts)
				.build();

			return this;
		},

		setData: function() {
			this.$el.data(instanceName, this);

			return this;
		},

		setOptions: function(opts) {
			this.options = $.extend( true, {}, PluginMarkdownEditor.defaults, opts );

			return this;
		},

		build: function() {
			this.$el.markdown( this.options );

			return this;
		}
	};

	// expose to scope
	$.extend(theme, {
		PluginMarkdownEditor: PluginMarkdownEditor
	});

	// jquery plugin
	$.fn.themePluginMarkdownEditor = function(opts) {
		return this.each(function() {
			var $this = $(this);

			if ($this.data(instanceName)) {
				return $this.data(instanceName);
			} else {
				return new PluginMarkdownEditor($this, opts);
			}

		});
	}

}).apply(this, [ window.theme, jQuery ]);

// Masked Input
(function(theme, $) {

	theme = theme || {};

	var instanceName = '__maskedInput';

	var PluginMaskedInput = function($el, opts) {
		return this.initialize($el, opts);
	};

	PluginMaskedInput.defaults = {
	};

	PluginMaskedInput.prototype = {
		initialize: function($el, opts) {
			if ( $el.data( instanceName ) ) {
				return this;
			}

			this.$el = $el;

			this
				.setData()
				.setOptions(opts)
				.build();

			return this;
		},

		setData: function() {
			this.$el.data(instanceName, this);

			return this;
		},

		setOptions: function(opts) {
			this.options = $.extend( true, {}, PluginMaskedInput.defaults, opts );

			return this;
		},

		build: function() {
			this.$el.mask( this.$el.data('input-mask'), this.options );

			return this;
		}
	};

	// expose to scope
	$.extend(theme, {
		PluginMaskedInput: PluginMaskedInput
	});

	// jquery plugin
	$.fn.themePluginMaskedInput = function(opts) {
		return this.each(function() {
			var $this = $(this);

			if ($this.data(instanceName)) {
				return $this.data(instanceName);
			} else {
				return new PluginMaskedInput($this, opts);
			}

		});
	}

}).apply(this, [ window.theme, jQuery ]);

// MaxLength
(function(theme, $) {

	theme = theme || {};

	var instanceName = '__maxlength';

	var PluginMaxLength = function($el, opts) {
		return this.initialize($el, opts);
	};

	PluginMaxLength.defaults = {
		alwaysShow: true,
		placement: 'bottom-left',
		warningClass: 'label label-success bottom-left',
		limitReachedClass: 'label label-danger bottom-left'
	};

	PluginMaxLength.prototype = {
		initialize: function($el, opts) {
			if ( $el.data( instanceName ) ) {
				return this;
			}

			this.$el = $el;

			this
				.setData()
				.setOptions(opts)
				.build();

			return this;
		},

		setData: function() {
			this.$el.data(instanceName, this);

			return this;
		},

		setOptions: function(opts) {
			this.options = $.extend( true, {}, PluginMaxLength.defaults, opts );

			return this;
		},

		build: function() {
			this.$el.maxlength( this.options );

			return this;
		}
	};

	// expose to scope
	$.extend(theme, {
		PluginMaxLength: PluginMaxLength
	});

	// jquery plugin
	$.fn.themePluginMaxLength = function(opts) {
		return this.each(function() {
			var $this = $(this);

			if ($this.data(instanceName)) {
				return $this.data(instanceName);
			} else {
				return new PluginMaxLength($this, opts);
			}

		});
	}

}).apply(this, [ window.theme, jQuery ]);

// MultiSelect
(function(theme, $) {

	theme = theme || {};

	var instanceName = '__multiselect';

	var PluginMultiSelect = function($el, opts) {
		return this.initialize($el, opts);
	};

	PluginMultiSelect.defaults = {
		templates: {
			filter: '
<input class="form-control multiselect-search" type="text">
'
		}
	};

	PluginMultiSelect.prototype = {
		initialize: function($el, opts) {
			if ( $el.data( instanceName ) ) {
				return this;
			}

			this.$el = $el;

			this
				.setData()
				.setOptions(opts)
				.build();

			return this;
		},

		setData: function() {
			this.$el.data(instanceName, this);

			return this;
		},

		setOptions: function(opts) {
			this.options = $.extend( true, {}, PluginMultiSelect.defaults, opts );

			return this;
		},

		build: function() {
			this.$el.multiselect( this.options );

			return this;
		}
	};

	// expose to scope
	$.extend(theme, {
		PluginMultiSelect: PluginMultiSelect
	});

	// jquery plugin
	$.fn.themePluginMultiSelect = function(opts) {
		return this.each(function() {
			var $this = $(this);

			if ($this.data(instanceName)) {
				return $this.data(instanceName);
			} else {
				return new PluginMultiSelect($this, opts);
			}

		});
	}

}).apply(this, [ window.theme, jQuery ]);

// Select2
(function(theme, $) {

	theme = theme || {};

	var instanceName = '__select2';

	var PluginSelect2 = function($el, opts) {
		return this.initialize($el, opts);
	};

	PluginSelect2.defaults = {
	};

	PluginSelect2.prototype = {
		initialize: function($el, opts) {
			if ( $el.data( instanceName ) ) {
				return this;
			}

			this.$el = $el;

			this
				.setData()
				.setOptions(opts)
				.build();

			return this;
		},

		setData: function() {
			this.$el.data(instanceName, this);

			return this;
		},

		setOptions: function(opts) {
			this.options = $.extend( true, {}, PluginSelect2.defaults, opts );

			return this;
		},

		build: function() {
			this.$el.select2( this.options );

			return this;
		}
	};

	// expose to scope
	$.extend(theme, {
		PluginSelect2: PluginSelect2
	});

	// jquery plugin
	$.fn.themePluginSelect2 = function(opts) {
		return this.each(function() {
			var $this = $(this);

			if ($this.data(instanceName)) {
				return $this.data(instanceName);
			} else {
				return new PluginSelect2($this, opts);
			}

		});
	}

}).apply(this, [ window.theme, jQuery ]);

// Spinner
(function(theme, $) {

	theme = theme || {};

	var instanceName = '__spinner';

	var PluginSpinner = function($el, opts) {
		return this.initialize($el, opts);
	};

	PluginSpinner.defaults = {
	};

	PluginSpinner.prototype = {
		initialize: function($el, opts) {
			if ( $el.data( instanceName ) ) {
				return this;
			}

			this.$el = $el;

			this
				.setData()
				.setOptions(opts)
				.build();

			return this;
		},

		setData: function() {
			this.$el.data(instanceName, this);

			return this;
		},

		setOptions: function(opts) {
			this.options = $.extend( true, {}, PluginSpinner.defaults, opts );

			return this;
		},

		build: function() {
			this.$el.spinner( this.options );

			return this;
		}
	};

	// expose to scope
	$.extend(theme, {
		PluginSpinner: PluginSpinner
	});

	// jquery plugin
	$.fn.themePluginSpinner = function(opts) {
		return this.each(function() {
			var $this = $(this);

			if ($this.data(instanceName)) {
				return $this.data(instanceName);
			} else {
				return new PluginSpinner($this, opts);
			}

		});
	}

}).apply(this, [ window.theme, jQuery ]);

// SummerNote
(function(theme, $) {

	theme = theme || {};

	var instanceName = '__summernote';

	var PluginSummerNote = function($el, opts) {
		return this.initialize($el, opts);
	};

	PluginSummerNote.defaults = {
		onfocus: function() {
			$( this ).closest( '.note-editor' ).addClass( 'active' );
		},
		onblur: function() {
			$( this ).closest( '.note-editor' ).removeClass( 'active' );
		}
	};

	PluginSummerNote.prototype = {
		initialize: function($el, opts) {
			if ( $el.data( instanceName ) ) {
				return this;
			}

			this.$el = $el;

			this
				.setData()
				.setOptions(opts)
				.build();

			return this;
		},

		setData: function() {
			this.$el.data(instanceName, this);

			return this;
		},

		setOptions: function(opts) {
			this.options = $.extend( true, {}, PluginSummerNote.defaults, opts );

			return this;
		},

		build: function() {
			this.$el.summernote( this.options );

			return this;
		}
	};

	// expose to scope
	$.extend(theme, {
		PluginSummerNote: PluginSummerNote
	});

	// jquery plugin
	$.fn.themePluginSummerNote = function(opts) {
		return this.each(function() {
			var $this = $(this);

			if ($this.data(instanceName)) {
				return $this.data(instanceName);
			} else {
				return new PluginSummerNote($this, opts);
			}

		});
	}

}).apply(this, [ window.theme, jQuery ]);

// TextArea AutoSize
(function(theme, $) {

	theme = theme || {};

	var initialized = false;
	var instanceName = '__textareaAutosize';

	var PluginTextAreaAutoSize = function($el, opts) {
		return this.initialize($el, opts);
	};

	PluginTextAreaAutoSize.defaults = {
	};

	PluginTextAreaAutoSize.prototype = {
		initialize: function($el, opts) {
			if (initialized) {
				return this;
			}

			this.$el = $el;

			this
				.setData()
				.setOptions(opts)
				.build();

			return this;
		},

		setData: function() {
			this.$el.data(instanceName, this);

			return this;
		},

		setOptions: function(opts) {
			this.options = $.extend( true, {}, PluginTextAreaAutoSize.defaults, opts );

			return this;
		},

		build: function() {
			this.$el.autosize( this.options );

			return this;
		}
	};

	// expose to scope
	$.extend(theme, {
		PluginTextAreaAutoSize: PluginTextAreaAutoSize
	});

	// jquery plugin
	$.fn.themePluginTextAreaAutoSize = function(opts) {
		return this.each(function() {
			var $this = $(this);

			if ($this.data(instanceName)) {
				return $this.data(instanceName);
			} else {
				return new PluginTextAreaAutoSize($this, opts);
			}

		});
	}

}).apply(this, [ window.theme, jQuery ]);

// TimePicker
(function(theme, $) {

	theme = theme || {};

	var instanceName = '__timepicker';

	var PluginTimePicker = function($el, opts) {
		return this.initialize($el, opts);
	};

	PluginTimePicker.defaults = {
		disableMousewheel: true
	};

	PluginTimePicker.prototype = {
		initialize: function($el, opts) {
			if ( $el.data( instanceName ) ) {
				return this;
			}

			this.$el = $el;

			this
				.setData()
				.setOptions(opts)
				.build();

			return this;
		},

		setData: function() {
			this.$el.data(instanceName, this);

			return this;
		},

		setOptions: function(opts) {
			this.options = $.extend( true, {}, PluginTimePicker.defaults, opts );

			return this;
		},

		build: function() {
			this.$el.timepicker( this.options );

			return this;
		}
	};

	// expose to scope
	$.extend(theme, {
		PluginTimePicker: PluginTimePicker
	});

	// jquery plugin
	$.fn.themePluginTimePicker = function(opts) {
		return this.each(function() {
			var $this = $(this);

			if ($this.data(instanceName)) {
				return $this.data(instanceName);
			} else {
				return new PluginTimePicker($this, opts);
			}

		});
	}

}).apply(this, [ window.theme, jQuery ]);

// Map Builder
(function( theme, $ ) {

	'use strict';

	// prevent undefined var
	theme = theme || {};

	// internal var to check if reached limit
	var timeouts = 0;

	// instance
	var instanceName = '__gmapbuilder';

	// private
	var roundNumber = function( number, precision ) {
		if( precision < 0 ) {
			precision = 0;
		} else if( precision > 10 ) {
			precision = 10;
		}
		var a = [ 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 10000000000 ];

		return Math.round( number * a[ precision ] ) / a[ precision ];
	};

	// definition
	var GMapBuilder = function( $wrapper, opts ) {
		return this.initialize( $wrapper, opts );
	};

	GMapBuilder.defaults = {
		mapSelector: '#gmap',

		markers: {
			modal: '#MarkerModal',
			list: '#MarkersList',
			removeAll: '#MarkerRemoveAll'
		},

		previewModal: '#ModalPreview',
		getCodeModal: '#ModalGetCode',

		mapOptions: {
			center: {
				lat: 1.340021,
				lng: 103.825583
			},
			zoomControl: true,
			zoom: 12,
            zoomControlOptions: {
                style: google.maps.ZoomControlStyle.DEFAULT
            },
            mapTypeControl: true,
            mapTypeControlOptions: {
                style: google.maps.MapTypeControlStyle.HORIZONTAL_BAR
            },
            scaleControl: false,
            scrollwheel: true,
            panControl: false,
            streetViewControl: false,
            draggable : true,
            overviewMapControl: false,
            mapTypeId: google.maps.MapTypeId.ROADMAP,
            styles: [{"featureType": "water","elementType": "geometry","stylers": [{ "color": "#193341" }]},{"featureType": "landscape","elementType": "geometry","stylers": [{ "color": "#2c5a71" }]},{"featureType": "road","elementType": "geometry","stylers": [{ "color": "#29768a" },{ "lightness": -37 }]},{"featureType": "poi","elementType": "geometry","stylers": [{ "color": "#406d80" }]},{"featureType": "transit","elementType": "geometry","stylers": [{ "color": "#406d80" }]},{"elementType": "labels.text.stroke","stylers": [{ "visibility": "on" },{ "color": "#3e606f" },{ "weight": 2 },{ "gamma": 0.84 }]},{"elementType": "labels.text.fill","stylers": [{ "color": "#ffffff" }]},{"featureType": "administrative","elementType": "geometry","stylers": [{ "weight": 0.6 },{ "color": "#1a3541" }]},{"elementType": "labels.icon","stylers": [{ "visibility": "off" }]},{"featureType": "poi.park","elementType": "geometry","stylers": [{ "color": "#2c5a71" }]}]
		}
	};

	GMapBuilder.prototype = {

		markers: [],

		initialize: function( $wrapper, opts ) {
			this.$wrapper = $wrapper;

			this
				.setData()
				.setOptions( opts )
				.setVars()
				.build()
				.events();

			return this;
		},

		setData: function() {
			this.$wrapper.data( instanceName, this );

			return this;
		},

		setOptions: function( opts ) {
			this.options = $.extend( true, {}, GMapBuilder.defaults, opts );

			return this;
		},

		setVars: function() {
			this.$mapContainer		= this.$wrapper.find( this.options.mapSelector );

			this.$previewModal		= $( this.options.previewModal );
			this.$getCodeModal		= $( this.options.getCodeModal );

			this.marker				= {};
			this.marker.$modal  	= $( this.options.markers.modal );
			this.marker.$form		= this.marker.$modal.find( 'form' );
			this.marker.$list		= $( this.options.markers.list );
			this.marker.$removeAll	= $( this.options.markers.removeAll );

			return this;
		},

		build: function() {
			var _self = this;

			if ( !!window.SnazzyThemes ) {
				var themeOpts = [];

				$.each( window.SnazzyThemes, function( i, theme ) {
					themeOpts.push( $('<option value="' + theme.id + '">' + theme.name + '</option>').data( 'json', theme.json ) );
				});

				this.$wrapper.find( '[data-builder-field="maptheme"]' ).append( themeOpts );
			}

			this.geocoder = new google.maps.Geocoder();

			google.maps.event.addDomListener( window, 'load', function() {
				_self.options.mapOptions.center = new google.maps.LatLng( _self.options.mapOptions.center.lat, _self.options.mapOptions.center.lng );

				_self.map = new google.maps.Map( _self.$mapContainer.get(0), _self.options.mapOptions );

				_self
					.updateControl( 'latlng' )
					.updateControl( 'zoomlevel' );
	            this.ctaLayer = new google.maps.KmlLayer({
                    url: 'http://accessibility.place/static/SG_SUBZONE.kml'
                });
                this.ctaLayer.setMap(_self.map);

				_self.mapEvents();
			});

			return this;
		},

		events: function() {
			var _self = this;

			this.$wrapper.find( '[data-builder-field]' ).each(function() {
				var $this = $( this ),
					field,
					value;

				field = $this.data( 'builder-field' );

				$this.on( 'change', function() {

					if ( $this.is( 'select' ) ) {
						value = $this.children( 'option:selected' ).val().toLowerCase();
					} else {
						value = $this.val().toLowerCase();
					}

					_self.updateMap( field, value );
				});

			});

			this.marker.$form.on( 'submit', function( e ) {
				e.preventDefault();

				_self.saveMarker( _self.marker.$form.formToObject() );
			});

			this.marker.$removeAll.on( 'click', function( e ) {
				e.preventDefault();
				_self.removeAllMarkers();
			});

			// preview events
			this.$previewModal.on( 'shown.bs.modal', function() {
				_self.preview();
			});

			this.$previewModal.on( 'hidden.bs.modal', function() {
				_self.$previewModal.find( 'iframe' ).get(0).contentWindow.document.body.innerHTML = '';
			});

			// get code events
			this.$getCodeModal.on( 'shown.bs.modal', function() {
				_self.getCode();
			});

			return this;
		},

		// MAP FUNCTIONS
		// -----------------------------------------------------------------------------
		mapEvents: function() {
			var _self = this;

			google.maps.event.addDomListener( _self.map, 'resize', function() {
				google.maps.event.trigger( _self.map, 'resize' );
			});

			google.maps.event.addListener( this.map, 'center_changed', function() {
				var coords = _self.map.getCenter();
				_self.updateControl( 'latlng', {
					lat: roundNumber( coords.lat(), 6 ),
					lng: roundNumber( coords.lng(), 6 )
				});
			});

			google.maps.event.addListener( this.map, 'zoom_changed', function() {
				_self.updateControl( 'zoomlevel', _self.map.getZoom() );
			});

			google.maps.event.addListener( this.map, 'maptypeid_changed', function() {
				_self.updateControl( 'maptype', _self.map.getMapTypeId() );
			});

			return this;
		},

		updateMap: function( prop, value ) {
			var updateFn;

			updateFn = this.updateMapProperty[ prop ];

			if ( $.isFunction( updateFn ) ) {
				updateFn.apply( this, [ value ] );
			} else {
				console.info( 'missing update function for', prop );
			}

			return this;
		},

		updateMapProperty: {

			latlng: function() {
				var lat,
					lng;

				lat = this.$wrapper.find('[data-builder-field][name="latitude"]').val();
				lng = this.$wrapper.find('[data-builder-field][name="longitude"]').val();

				if ( lat.length > 0 && lng.length > 0 ) {
					this.map.setCenter( new google.maps.LatLng( lat, lng ) );
				}

				return this;
			},

			zoomlevel: function( value ) {
				var value = arguments[ 0 ];

				this.map.setZoom( parseInt( value, 10 ) );

				return this;
			},

			maptypecontrol: function( value ) {
				var options;

				options	= {};

				if ( value === 'false' ){
					options.mapTypeControl = false;
				} else {
					options = {
						mapTypeControl: true,
						mapTypeControlOptions: {
							style: google.maps.MapTypeControlStyle[ value.toUpperCase() ]
						}
					};
				}

				this.map.setOptions( options );

				return this;
			},

			zoomcontrol: function( value ) {
				var options;

				options	= {};

				if ( value === 'false' ){
					options.zoomControl = false;
				} else {
					options = {
						zoomControl: true,
						zoomControlOptions: {
							style: google.maps.ZoomControlStyle[ value.toUpperCase() ]
						}
					};
				}

				this.map.setOptions( options );

				return this;
			},

			scalecontrol: function( value ) {
				var options;

				options	= {};

				options.scaleControl = value !== 'false';

				this.map.setOptions( options );

				return this;
			},

			streetviewcontrol: function( value ) {
				var options;

				options	= {};

				options.streetViewControl = value !== 'false';

				this.map.setOptions( options );

				return this;
			},

			pancontrol: function( value ) {
				var options;

				options	= {};

				options.panControl = value !== 'false';

				this.map.setOptions( options );

				return this;
			},

			overviewcontrol: function( value ) {
				var options;

				options	= {};

				if ( value === 'false' ){
					options.overviewMapControl = false;
				} else {
					options = {
						overviewMapControl: true,
						overviewMapControlOptions: {
							opened: value === 'opened'
						}
					};
				}

				this.map.setOptions( options );

				return this;
			},

			draggablecontrol: function( value ) {
				var options;

				options	= {};

				options.draggable = value !== 'false';

				this.map.setOptions( options );

				return this;
			},

			clicktozoomcontrol: function( value ) {
				var options;

				options	= {};

				options.disableDoubleClickZoom = value === 'false';

				this.map.setOptions( options );

				return this;
			},

			scrollwheelcontrol: function( value ) {
				var options;

				options	= {};

				options.scrollwheel = value !== 'false';

				this.map.setOptions( options );

				return this;
			},

			maptype: function( value ) {
				var options,
					mapStyles,
					mapType;

				mapStyles = this.$wrapper.find( '[data-builder-field="maptheme"]' ).children( 'option' ).filter( ':selected' ).data( 'json' );
				mapType =  google.maps.MapTypeId[ value.toUpperCase() ];

				options	= {
					mapTypeId: mapType
				};

				if ( $.inArray( google.maps.MapTypeId[ value.toUpperCase() ], [ 'terrain', 'roadmap' ]) > -1 && !!mapStyles ) {
					options.styles = eval( mapStyles );
				} else {
					options.styles = false;
					this.updateControl( 'maptheme' );
				}

				this.map.setOptions( options );
			},

			maptheme: function( value ) {
				var json,
					mapType,
					options;

				mapType = google.maps.MapTypeId[ this.map.getMapTypeId() === 'terrain' ? 'TERRAIN' : 'ROADMAP' ];
				options = {};
				json = this.$wrapper.find( '[data-builder-field="maptheme"]' ).children( 'option' ).filter( ':selected' ).data( 'json' );

				if ( !json ) {
					options = {
						mapTypeId: mapType,
						styles: false
					};
				} else {
					options = {
						mapTypeId: mapType,
						styles: eval( json )
					};
				}

				this.map.setOptions( options );
			}

		},

		// CONTROLS FUNCTIONS
		// -----------------------------------------------------------------------------
		updateControl: function( prop ) {
			var updateFn;

			updateFn = this.updateControlValue[ prop ];

			if ( $.isFunction( updateFn ) ) {
				updateFn.apply( this );
			} else {
				console.info( 'missing update function for', prop );
			}

			return this;
		},

		updateControlValue: {

			latlng: function() {
				var center = this.map.getCenter();

				this.$wrapper.find('[data-builder-field][name="latitude"]').val( roundNumber( center.lat() , 6 ) );
				this.$wrapper.find('[data-builder-field][name="longitude"]').val( roundNumber( center.lng() , 6 ) );
			},

			zoomlevel: function() {
				var $control,
					level;

				level = this.map.getZoom();

				$control = this.$wrapper.find('[data-builder-field="zoomlevel"]');

				$control
					.children( 'option[value="' + level + '"]' )
					.prop( 'selected', true );

				if ( $control.hasClass( 'select2-offscreen' ) ) {
					$control.select2( 'val', level );
				}
			},

			maptype: function() {
				var $control,
					mapType;

				mapType = this.map.getMapTypeId();
				$control = this.$wrapper.find('[data-builder-field="maptype"]');

				$control
					.children( 'option[value="' + mapType + '"]' )
					.prop( 'selected', true );

				if ( $control.hasClass( 'select2-offscreen' ) ) {
					$control.select2( 'val', mapType );
				}
			},

			maptheme: function() {
				var $control;

				$control = this.$wrapper.find('[data-builder-field="maptheme"]');

				$control
					.children( 'option[value="false"]' )
					.prop( 'selected', true );

				if ( $control.hasClass( 'select2-offscreen' ) ) {
					$control.select2( 'val', 'false' );
				}
			}

		},

		// MARKERS FUNCTIONS
		// -----------------------------------------------------------------------------
		editMarker: function( marker ) {
			this.currentMarker = marker;

			this.marker.$form
				.find( '#MarkerLocation' ).val( marker.location );

			this.marker.$form
				.find( '#MarkerTitle' ).val( marker.title );

			this.marker.$form
				.find( '#MarkerDescription' ).val( marker.description );

			this.marker.$modal.modal( 'show' );
		},

		removeMarker: function( marker ) {
			var i;

			marker._instance.setMap( null );
			marker._$html.remove();

			for( i = 0; i < this.markers.length; i++ ) {
				if ( marker === this.markers[ i ] ) {
					this.markers.splice( i, 1 );
					break;
				}
			}

			if ( this.markers.length === 0 ) {
				this.marker.$list.addClass( 'hidden' );
			}
		},

		saveMarker: function( marker ) {
			this._geocode( marker );
		},

		removeAllMarkers: function() {
			var i = 0,
				l,
				marker;

			l = this.markers.length;

			for( ; i < l; i++ ) {
				marker = this.markers[ i ];

				marker._instance.setMap( null );
				marker._$html.remove();
			}

			this.markers = [];
			this.marker.$list.addClass( 'hidden' );
		},

		_geocode: function( marker ) {
			var _self = this,
				status;

			this.geocoder.geocode({ address: marker.location }, function( response, status ) {
				_self._onGeocodeResult( marker, response, status );
			});
		},

		_onGeocodeResult: function( marker, response, status ) {
			var result;

			if ( !response || status !== google.maps.GeocoderStatus.OK ) {
				if ( status == google.maps.GeocoderStatus.ZERO_RESULTS ) {
					// show notification
				} else {
					timeouts++;
					if ( timeouts > 3 ) {
						// show notification reached limit of requests
					}
				}
			} else {
				timeouts = 0;

				if ( this.currentMarker ) {
					this.removeMarker( this.currentMarker );
					this.currentMarker = null;
				}

				// grab first result of the list
				result = response[ 0 ];

				// get lat & lng and set to marker
				marker.lat = Math.round( result.geometry.location.lat() * 1000000 ) / 1000000;
				marker.lng = Math.round( result.geometry.location.lng() * 1000000 ) / 1000000;

				var opts = {
					position: new google.maps.LatLng( marker.lat, marker.lng ),
					map: this.map
				};

				if ( marker.title.length > 0 ) {
					opts.title = marker.title;
				}

				if ( marker.description.length > 0 ) {
					opts.desc = marker.description;
				}

				marker.position = opts.position;
				marker._instance = new google.maps.Marker( opts );

				if ( !!marker.title || !!marker.description  ) {
					this._bindMarkerClick( marker );
				}

				this.markers.push( marker );

				// append to markers list
				this._appendMarkerToList( marker );

				// hide modal and reset form
				this.marker.$form.get(0).reset();
				this.marker.$modal.modal( 'hide' );
			}
		},

		_appendMarkerToList: function( marker ) {
			var _self = this,
				html;

			html = [
				'
',
					'
{location}
',
					'',
					'',
					'',
				'
'
			].join('');

			html = html.replace( /\{location\}/, !!marker.title ? marker.title : marker.location );

			marker._$html = $( html );

			// events
			marker._$html.find( '.location-center' )
				.on( 'click', function( e ) {
					_self.map.setCenter( marker.position );
				});

			marker._$html.find( '.location-remove' )
				.on( 'click', function( e ) {
					e.preventDefault();
					_self.removeMarker( marker );
				});

			marker._$html.find( '.location-edit' )
				.on( 'click', function( e ) {
					e.preventDefault();
					_self.editMarker( marker );
				});

			this.marker.$list
				.append( marker._$html )
				.removeClass( 'hidden' );
		},

		_bindMarkerClick: function( marker ) {
			var _self = this,
				html;

			html = [
				'
',
					'{title}',
					'{description}',
				'
'
			].join('');

			html = html.replace(/\{title\}/, !!marker.title ?  ("
" + marker.title + "
") : "" );
			html = html.replace(/\{description\}/, !!marker.description ?  ("
" + marker.description + "
") : "" );

			marker._infoWindow = new google.maps.InfoWindow({ content: html });

			google.maps.event.addListener( marker._instance, 'click', function() {

				if ( marker._infoWindow.isOpened ) {
					marker._infoWindow.close();
					marker._infoWindow.isOpened = false;
				} else {
					marker._infoWindow.open( _self.map, this );
					marker._infoWindow.isOpened = true;
				}

			});
		},

		preview: function() {
			var customScript,
				googleScript,
				iframe,
				previewHtml;

			previewHtml = [
				'',
				'

'
			];

			iframe = this.$previewModal.find( 'iframe' ).get(0).contentWindow.document;

			iframe.body.innerHTML = previewHtml.join('');

			customScript = iframe.createElement( 'script' );
			customScript.type = 'text/javascript';
			customScript.text = "window.initialize = function() { " + this.generate() + " init(); }; ";
			iframe.body.appendChild( customScript );

			googleScript = iframe.createElement( 'script' );
			googleScript.type = 'text/javascript';
			googleScript.text = 'function loadScript() { var script = document.createElement("script"); script.type = "text/javascript"; script.src = "http://maps.googleapis.com/maps/api/js?key=&sensor=&callback=initialize"; document.body.appendChild(script); } loadScript()';
			iframe.body.appendChild( googleScript );
		},

		getCode: function() {
			this.$getCodeModal.find('.modal-body pre').html( this.generate().replace( /</g, '<' ).replace( />/g, '>' ) );
		},

		// GENERATE CODE
		// -----------------------------------------------------------------------------
		generate: function() {
			var i,
				work;

			var output = [
				'    google.maps.event.addDomListener(window, "load", init);',
				'    var map;',
				'    function init() {',
				'        var mapOptions = {',
				'            center: new google.maps.LatLng({lat}, {lng}),',
				'            zoom: {zoom},',
				'            zoomControl: {zoomControl},',
				'            {zoomControlOptions}',
				'            disableDoubleClickZoom: {disableDoubleClickZoom},',
				'            mapTypeControl: {mapTypeControl},',
				'            {mapTypeControlOptions}',
				'            scaleControl: {scaleControl},',
				'            scrollwheel: {scrollwheel},',
				'            panControl: {panControl},',
				'            streetViewControl: {streetViewControl},',
				'            draggable : {draggable},',
				'            overviewMapControl: {overviewMapControl},',
				'            {overviewMapControlOptions}',
				'            mapTypeId: google.maps.MapTypeId.{mapTypeId}{styles}',
				'        };',
				'',
				'        var mapElement = document.getElementById("{mapid}");',
				'        var map = new google.maps.Map(mapElement, mapOptions);',
				'        {locations}',
				'    }'
			];

			output = output.join("\r\n");

			var zoomControl			= this.$wrapper.find('[data-builder-field="zoomcontrol"] option:selected').val() !== 'false';
			var mapTypeControl		= this.$wrapper.find('[data-builder-field="maptypecontrol"] option:selected').val() !== 'false';
			var overviewMapControl	= this.$wrapper.find('[data-builder-field="overviewcontrol"] option:selected').val().toLowerCase();
			var $themeControl		= this.$wrapper.find('[data-builder-field="maptheme"] option:selected').filter( ':selected' );

			output = output
						.replace( /\{mapid\}/, this.$wrapper.find('[data-builder-field="mapid"]').val() )
						.replace( /\{lat\}/, this.$wrapper.find('[data-builder-field][name="latitude"]').val() )
						.replace( /\{lng\}/, this.$wrapper.find('[data-builder-field][name="longitude"]').val() )
						.replace( /\{zoom\}/, this.$wrapper.find('[data-builder-field="zoomlevel"] option:selected').val() )
						.replace( /\{zoomControl\}/, zoomControl )
						.replace( /\{disableDoubleClickZoom\}/, this.$wrapper.find('[data-builder-field="clicktozoomcontrol"] option:selected').val() === 'false' )
						.replace( /\{mapTypeControl\}/, mapTypeControl )
						.replace( /\{scaleControl\}/, this.$wrapper.find('[data-builder-field="scalecontrol"] option:selected').val() !== 'false' )
						.replace( /\{scrollwheel\}/, this.$wrapper.find('[data-builder-field="scrollwheelcontrol"] option:selected').val() !== 'false' )
						.replace( /\{panControl\}/, this.$wrapper.find('[data-builder-field="pancontrol"] option:selected').val() !== 'false' )
						.replace( /\{streetViewControl\}/, this.$wrapper.find('[data-builder-field="streetviewcontrol"] option:selected').val() !== 'false' )
						.replace( /\{draggable\}/, this.$wrapper.find('[data-builder-field="draggablecontrol"] option:selected').val() !== 'false' )
						.replace( /\{overviewMapControl\}/, overviewMapControl !== 'false' )
						.replace( /\{mapTypeId\}/, this.$wrapper.find('[data-builder-field="maptype"] option:selected').val().toUpperCase() );

			if ( zoomControl ) {
				work = {
					zoomControlOptions: {
						style: this.$wrapper.find('[data-builder-field="maptypecontrol"] option:selected').val().toUpperCase()
					}
				};
				output = output.replace( /\{zoomControlOptions\}/, "zoomControlOptions: {\r\n                style: google.maps.ZoomControlStyle." + this.$wrapper.find('[data-builder-field="zoomcontrol"] option:selected').val().toUpperCase() + "\r\n\            },");
			} else {
				output = output.replace( /\{zoomControlOptions\}/, '' );
			}

			if ( mapTypeControl ) {
				work = {
					zoomControlOptions: {
						style: this.$wrapper.find('[data-builder-field="maptypecontrol"] option:selected').val().toUpperCase()
					}
				};
				output = output.replace( /\{mapTypeControlOptions\}/, "mapTypeControlOptions: {\r\n                style: google.maps.MapTypeControlStyle." + this.$wrapper.find('[data-builder-field="maptypecontrol"] option:selected').val().toUpperCase() + "\r\n\            },");
			} else {
				output = output.replace( /\{mapTypeControlOptions\}/, '' );
			}

			if ( overviewMapControl !== 'false' ) {
				output = output.replace( /\{overviewMapControlOptions\}/, "overviewMapControlOptions: {\r\n                opened: " + (overviewMapControl === 'opened') + "\r\n\            },");
			} else {
				output = output.replace( /\{overviewMapControlOptions\}/, '' );
			}

			if ( $themeControl.val() !== 'false' ) {
				output = output.replace( /\{styles\}/, ',\r\n            styles: ' + $themeControl.data( 'json' ).replace(/\r\n/g, '') );
			} else {
				output = output.replace( /\{styles\}/, '' );
			}

			if ( this.markers.length > 0 ) {
				var work = [ 'var locations = [' ];
				var m,
					object;

				for( i = 0; i < this.markers.length; i++ ) {
					m = this.markers[ i ];
					object = '';

					object += '            { lat: ' + m.lat + ', lng: ' + m.lng;

					if ( !!m.title ) {
						object += ', title: "' + m.title + '"';
					}

					if ( !!m.description ) {
						object += ', description: "' + m.description + '"';
					}

					object += ' }';

					if ( i + 1 < this.markers.length ) {
						object += ',';
					}

					work.push( object );
				}

				work.push( '        ];\r\n' )

				work.push( '        var opts = {};' )
				work.push( '        for (var i = 0; i < locations.length; i++) {' );
				work.push( '            opts.position = new google.maps.LatLng( locations[ i ].lat, locations[ i ].lng );' );
				work.push( '            opts.map = map;' );
				work.push( '            if ( !!locations[ i ] .title ) { opts.title = locations[ i ].title; }');
				work.push( '            if ( !!locations[ i ] .description ) { opts.description = locations[ i ].description; }');
				work.push( '            marker = new google.maps.Marker( opts );' );
				work.push( '' );
				work.push( '            (function() {' );
				work.push( '                var html = [' );
				work.push( '                	\'
\',' );
				work.push( '                		\'{title}\',' );
				work.push( '                		\'{description}\',' );
				work.push( '                	\'
\'' );
				work.push( '                ].join(\'\');' );

				work.push( '' );
				work.push( '                html = html.replace(/\{title\}/, !!opts.title ?  ("
" + opts.title + "
") : "" );' );
				work.push( '                html = html.replace(/\{description\}/, !!opts.description ?  ("
" + opts.description + "
") : "" );' );

				work.push( '                var infoWindow = new google.maps.InfoWindow({ content: html });' );

				work.push( '                google.maps.event.addListener( marker, \'click\', function() {' );
				work.push( '                	if ( infoWindow.isOpened ) {' );
				work.push( '                		infoWindow.close();' );
				work.push( '                		infoWindow.isOpened = false;' );
				work.push( '                	} else {' );
				work.push( '                		infoWindow.open( map, this );' );
				work.push( '                		infoWindow.isOpened = true;' );
				work.push( '                	}' );
				work.push( '                });' );
				work.push( '            })();' )
				work.push( '        }');

				output = output.replace( /\{locations\}/, work.join('\r\n') );
			} else {
				output = output.replace( /\{locations\}/, '' );
			}

			console.log( output );

			return output;
		}
	};

	// expose
	$.extend( true, theme, {
		Maps: {
			GMapBuilder: GMapBuilder
		}
	});

	// jQuery plugin
	$.fn.themeGMapBuilder = function( opts ) {
		return this.map(function() {
			var $this = $( this ),
				instance;

			instance = $this.data( instanceName );

			if ( instance ) {
				return instance;
			} else {
				return (new GMapBuilder( $this, opts ));
			}
		});
	};

	// auto initialize
	$(function() {
		$('[data-theme-gmap-builder]').each(function() {
			var $this = $( this );

			window.builder = $this.themeGMapBuilder();
		});
	});

}).apply(this, [ window.theme, jQuery ]);

// Animate
(function(theme, $) {

	theme = theme || {};

	var instanceName = '__animate';

	var PluginAnimate = function($el, opts) {
		return this.initialize($el, opts);
	};

	PluginAnimate.defaults = {
		accX: 0,
		accY: -150,
		delay: 1
	};

	PluginAnimate.prototype = {
		initialize: function($el, opts) {
			if ( $el.data( instanceName ) ) {
				return this;
			}

			this.$el = $el;

			this
				.setData()
				.setOptions(opts)
				.build();

			return this;
		},

		setData: function() {
			this.$el.data(instanceName, this);

			return this;
		},

		setOptions: function(opts) {
			this.options = $.extend(true, {}, PluginAnimate.defaults, opts, {
				wrapper: this.$el
			});

			return this;
		},

		build: function() {
			var self = this,
				$el = this.options.wrapper,
				delay = 0;

			$el.addClass('appear-animation');

			if(!$('html').hasClass('no-csstransitions') && $(window).width() > 767) {

				$el.appear(function() {

					delay = ($el.attr('data-appear-animation-delay') ? $el.attr('data-appear-animation-delay') : self.options.delay);

					if(delay > 1) {
						$el.css('animation-delay', delay + 'ms');
					}

					$el.addClass($el.attr('data-appear-animation'));

					setTimeout(function() {
						$el.addClass('appear-animation-visible');
					}, delay);

				}, {accX: self.options.accX, accY: self.options.accY});

			} else {

				$el.addClass('appear-animation-visible');

			}

			return this;
		}
	};

	// expose to scope
	$.extend(theme, {
		PluginAnimate: PluginAnimate
	});

	// jquery plugin
	$.fn.themePluginAnimate = function(opts) {
		return this.map(function() {
			var $this = $(this);

			if ($this.data(instanceName)) {
				return $this.data(instanceName);
			} else {
				return new PluginAnimate($this, opts);
			}

		});
	};

}).apply(this, [ window.theme, jQuery ]);

// Carousel
(function(theme, $) {

	theme = theme || {};

	var initialized = false;
	var instanceName = '__carousel';

	var PluginCarousel = function($el, opts) {
		return this.initialize($el, opts);
	};

	PluginCarousel.defaults = {
		itemsDesktop: false,
		itemsDesktopSmall: false,
		itemsTablet: false,
		itemsTabletSmall: false,
		itemsMobile: false
	};

	PluginCarousel.prototype = {
		initialize: function($el, opts) {
			if ( $el.data( instanceName ) ) {
				return this;
			}

			this.$el = $el;

			this
				.setData()
				.setOptions(opts)
				.build();

			return this;
		},

		setData: function() {
			this.$el.data(instanceName, this);

			return this;
		},

		setOptions: function(opts) {
			this.options = $.extend(true, {}, PluginCarousel.defaults, opts, {
				wrapper: this.$el
			});

			return this;
		},

		build: function() {
			this.options.wrapper.owlCarousel(this.options).addClass("owl-carousel-init");

			return this;
		}
	};

	// expose to scope
	$.extend(theme, {
		PluginCarousel: PluginCarousel
	});

	// jquery plugin
	$.fn.themePluginCarousel = function(opts) {
		return this.map(function() {
			var $this = $(this);

			if ($this.data(instanceName)) {
				return $this.data(instanceName);
			} else {
				return new PluginCarousel($this, opts);
			}

		});
	}

}).apply(this, [ window.theme, jQuery ]);

// Chart Circular
(function(theme, $) {

	theme = theme || {};

	var instanceName = '__chartCircular';

	var PluginChartCircular = function($el, opts) {
		return this.initialize($el, opts);
	};

	PluginChartCircular.defaults = {
		accX: 0,
		accY: -150,
		delay: 1,
		barColor: '#0088CC',
		trackColor: '#f2f2f2',
		scaleColor: false,
		scaleLength: 5,
		lineCap: 'round',
		lineWidth: 13,
		size: 175,
		rotate: 0,
		animate: ({
			duration: 2500,
			enabled: true
		})
	};

	PluginChartCircular.prototype = {
		initialize: function($el, opts) {
			if ( $el.data( instanceName ) ) {
				return this;
			}

			this.$el = $el;

			this
				.setData()
				.setOptions(opts)
				.build();

			return this;
		},

		setData: function() {
			this.$el.data(instanceName, this);

			return this;
		},

		setOptions: function(opts) {
			this.options = $.extend(true, {}, PluginChartCircular.defaults, opts, {
				wrapper: this.$el
			});

			return this;
		},

		build: function() {
			var self = this,
				$el = this.options.wrapper,
				value = ($el.attr('data-percent') ? $el.attr('data-percent') : 0),
				percentEl = $el.find('.percent'),
				shouldAnimate,
				data;

			shouldAnimate = $.isFunction($.fn[ 'appear' ]) && ( typeof $.browser !== 'undefined' && !$.browser.mobile );
			data = { accX: self.options.accX, accY: self.options.accY };

			$.extend(true, self.options, {
				onStep: function(from, to, currentValue) {
					percentEl.html(parseInt(currentValue));
				}
			});

			$el.attr('data-percent', (shouldAnimate ? 0 : value) );

			$el.easyPieChart( this.options );

			if ( shouldAnimate ) {
				$el.appear(function() {
					setTimeout(function() {
						$el.data('easyPieChart').update(value);
						$el.attr('data-percent', value);

					}, self.options.delay);
				}, data);
			} else {
				$el.data('easyPieChart').update(value);
				$el.attr('data-percent', value);
			}

			return this;
		}
	};

	// expose to scope
	$.extend(true, theme, {
		Chart: {
			PluginChartCircular: PluginChartCircular
		}
	});

	// jquery plugin
	$.fn.themePluginChartCircular = function(opts) {
		return this.map(function() {
			var $this = $(this);

			if ($this.data(instanceName)) {
				return $this.data(instanceName);
			} else {
				return new PluginChartCircular($this, opts);
			}

		});
	}

}).apply(this, [ window.theme, jQuery ]);

// Lightbox
(function(theme, $) {

	theme = theme || {};

	var instanceName = '__lightbox';

	var PluginLightbox = function($el, opts) {
		return this.initialize($el, opts);
	};

	PluginLightbox.defaults = {
		tClose: 'Close (Esc)', // Alt text on close button
		tLoading: 'Loading...', // Text that is displayed during loading. Can contain %curr% and %total% keys
		gallery: {
			tPrev: 'Previous (Left arrow key)', // Alt text on left arrow
			tNext: 'Next (Right arrow key)', // Alt text on right arrow
			tCounter: '%curr% of %total%' // Markup for "1 of 7" counter
		},
		image: {
			tError: 'The image could not be loaded.' // Error message when image could not be loaded
		},
		ajax: {
			tError: 'The content could not be loaded.' // Error message when ajax request failed
		}
	};

	PluginLightbox.prototype = {
		initialize: function($el, opts) {
			if ( $el.data( instanceName ) ) {
				return this;
			}

			this.$el = $el;

			this
				.setData()
				.setOptions(opts)
				.build();

			return this;
		},

		setData: function() {
			this.$el.data(instanceName, this);

			return this;
		},

		setOptions: function(opts) {
			this.options = $.extend(true, {}, PluginLightbox.defaults, opts, {
				wrapper: this.$el
			});

			return this;
		},

		build: function() {
			this.options.wrapper.magnificPopup(this.options);

			return this;
		}
	};

	// expose to scope
	$.extend(theme, {
		PluginLightbox: PluginLightbox
	});

	// jquery plugin
	$.fn.themePluginLightbox = function(opts) {
		return this.each(function() {
			var $this = $(this);

			if ($this.data(instanceName)) {
				return $this.data(instanceName);
			} else {
				return new PluginLightbox($this, opts);
			}

		});
	}

}).apply(this, [ window.theme, jQuery ]);

// Portlets
(function(theme, $) {

	theme = theme || {};

	var instanceName = '__portlet',
		storageOrderKey = '__portletOrder',
		storageStateKey = '__portletState';

	var PluginPortlet = function($el, opts) {
		return this.initialize($el, opts);
	};

	PluginPortlet.defaults = {
		connectWith: '[data-plugin-portlet]',
		items: '[data-portlet-item]',
		handle: '.portlet-handler',
		opacity: 0.7,
		placeholder: 'portlet-placeholder',
		cancel: 'portlet-cancel',
		forcePlaceholderSize: true,
		forceHelperSize: true,
		tolerance: 'pointer',
		helper: 'original',
		revert: 200
	};

	PluginPortlet.prototype = {
		initialize: function($el, opts) {
			if ( $el.data( instanceName ) ) {
				return this;
			}

			this.$el = $el;

			this
				.setData()
				.setOptions(opts)
				.build();

			return this;
		},

		setData: function() {
			this.$el.data(instanceName, this);

			return this;
		},

		setOptions: function(opts) {
			var _self = this;

			this.options = $.extend(true, {}, PluginPortlet.defaults, opts, {
				wrapper: this.$el,
				update: _self.onUpdate,
				create: _self.onLoad
			});

			return this;
		},

		onUpdate: function(event, ui) {
			var key = storageOrderKey,
				data = store.get(key),
				$this = $(this),
				porletId = $this.prop('id');

			if (!data) {
				data = {};
			}

			if (!!porletId) {
				data[porletId] = $this.sortable('toArray');
				store.set(key, data);
			}
		},

		onLoad: function(event, ui) {
			var key = storageOrderKey,
				data = store.get(key),
				$this = $(this),
				porletId = $this.prop('id'),
				portlet = $('#' + porletId);

			if (!!data) {
				var panels = data[porletId];

				if (!!panels) {
					$.each(panels, function(index, panelId) {
						$('#' + panelId).appendTo(portlet);
					});
				}
			}
		},

		saveState: function( panel ) {
			var key = storageStateKey,
				data = store.get(key),
				panelId = panel.prop('id');

			if (!data) {
				data = {};
			}

			if (!panelId) {
				return this;
			}

			var collapse = panel.find('.panel-actions').children('a.fa-caret-up, a.fa-caret-down'),
				isCollapsed = !!collapse.hasClass('fa-caret-up'),
				isRemoved = !panel.closest('body').get(0);

			if (isRemoved) {
				data[panelId] = 'removed';
			} else if (isCollapsed) {
				data[panelId] = 'collapsed';
			} else {
				delete data[panelId];
			}

			store.set(key, data);
			return this;
		},

		loadState: function() {
			var key = storageStateKey,
				data = store.get(key);

			if (!!data) {
				$.each(data, function(panelId, state) {
					var panel = $('#' + panelId);
					if (!panel.data('portlet-state-loaded')) {
						if (state == 'collapsed') {
							panel.find('.panel-actions a.fa-caret-down').trigger('click');
						} else if (state == 'removed') {
							panel.find('.panel-actions a.fa-times').trigger('click');
						}
						panel.data('portlet-state-loaded', true);
					}
				});
			}

			return this;
		},

		build: function() {
			var _self = this;

			if ( $.isFunction( $.fn.sortable ) ) {
				this.$el.sortable( this.options );
				this.$el.find('[data-portlet-item]').each(function() {
					_self.events( $(this) );
				});
			}

			var portlet = this.$el;
			portlet.css('min-height', 150);

			return this;
		},

		events: function($el) {
			var _self = this,
				portlet = $el.closest('[data-plugin-portlet]');

			this.loadState();

			$el.find('.panel-actions').on( 'click', 'a.fa-caret-up, a.fa-caret-down, a.fa-times', function( e ) {
				setTimeout(function() {
					_self.saveState( $el );
				}, 250);
			});

			return this;
		}
	};

	// expose to scope
	$.extend(theme, {
		PluginPortlet: PluginPortlet
	});

	// jquery plugin
	$.fn.themePluginPortlet = function(opts) {
		return this.map(function() {
			var $this = $(this);

			if ($this.data(instanceName)) {
				return $this.data(instanceName);
			} else {
				return new PluginPortlet($this, opts);
			}

		});
	}

}).apply(this, [ window.theme, jQuery ]);

// Scrollable
(function(theme, $) {

	theme = theme || {};

	var instanceName = '__scrollable';

	var PluginScrollable = function($el, opts) {
		return this.initialize($el, opts);
	};

	PluginScrollable.defaults = {
		contentClass: 'scrollable-content',
		paneClass: 'scrollable-pane',
		sliderClass: 'scrollable-slider',
		alwaysVisible: true,
		preventPageScrolling: true
	};

	PluginScrollable.prototype = {
		initialize: function($el, opts) {
			if ( $el.data( instanceName ) ) {
				return this;
			}

			this.$el = $el;

			this
				.setData()
				.setOptions(opts)
				.build();

			return this;
		},

		setData: function() {
			this.$el.data(instanceName, this);

			return this;
		},

		setOptions: function(opts) {
			this.options = $.extend(true, {}, PluginScrollable.defaults, opts, {
				wrapper: this.$el
			});

			return this;
		},

		build: function() {
			this.options.wrapper.nanoScroller(this.options);

			return this;
		}
	};

	// expose to scope
	$.extend(theme, {
		PluginScrollable: PluginScrollable
	});

	// jquery plugin
	$.fn.themePluginScrollable = function(opts) {
		return this.each(function() {
			var $this = $(this);

			if ($this.data(instanceName)) {
				return $this.data(instanceName);
			} else {
				return new PluginScrollable($this, opts);
			}

		});
	}

}).apply(this, [ window.theme, jQuery ]);

// Slider
(function(theme, $) {

	theme = theme || {};

	var instanceName = '__slider';

	var PluginSlider = function($el, opts) {
		return this.initialize($el, opts);
	};

	PluginSlider.defaults = {

	};

	PluginSlider.prototype = {
		initialize: function($el, opts) {
			if ( $el.data( instanceName ) ) {
				return this;
			}

			this.$el = $el;

			this
				.setVars()
				.setData()
				.setOptions(opts)
				.build();

			return this;
		},

		setVars: function() {
			var $output = $( this.$el.data('plugin-slider-output') );
			this.$output = $output.get(0) ? $output : null;

			return this;
		},

		setData: function() {
			this.$el.data(instanceName, this);

			return this;
		},

		setOptions: function(opts) {
			var _self = this;
			this.options = $.extend( true, {}, PluginSlider.defaults, opts );

			if ( this.$output ) {
				$.extend( this.options, {
					slide: function( event, ui ) {
						_self.onSlide( event, ui );
					}
				});
			}

			return this;
		},

		build: function() {
			this.$el.slider( this.options );

			return this;
		},

		onSlide: function( event, ui ) {
			if ( !ui.values ) {
				this.$output.val( ui.value );
			} else {
				this.$output.val( ui.values[ 0 ] + '/' + ui.values[ 1 ] );
			}

			this.$output.trigger('change');
		}
	};

	// expose to scope
	$.extend(theme, {
		PluginSlider: PluginSlider
	});

	// jquery plugin
	$.fn.themePluginSlider = function(opts) {
		return this.each(function() {
			var $this = $(this);

			if ($this.data(instanceName)) {
				return $this.data(instanceName);
			} else {
				return new PluginSlider($this, opts);
			}

		});
	}

}).apply(this, [ window.theme, jQuery ]);

// Toggle
(function(theme, $) {

	theme = theme || {};

	var instanceName = '__toggle';

	var PluginToggle = function($el, opts) {
		return this.initialize($el, opts);
	};

	PluginToggle.defaults = {
		duration: 350,
		isAccordion: false,
		addIcons: true
	};

	PluginToggle.prototype = {
		initialize: function($el, opts) {
			if ( $el.data( instanceName ) ) {
				return this;
			}

			this.$el = $el;

			this
				.setData()
				.setOptions(opts)
				.build();

			return this;
		},

		setData: function() {
			this.$el.data(instanceName, this);

			return this;
		},

		setOptions: function(opts) {
			this.options = $.extend(true, {}, PluginToggle.defaults, opts, {
				wrapper: this.$el
			});

			return this;
		},

		build: function() {
			var self = this,
				$wrapper = this.options.wrapper,
				$items = $wrapper.find('.toggle'),
				$el = null;

			$items.each(function() {
				$el = $(this);

				if(self.options.addIcons) {
					$el.find('> label').prepend(
						$('').addClass('fa fa-plus'),
						$('').addClass('fa fa-minus')
					);
				}

				if($el.hasClass('active')) {
					$el.find('> p').addClass('preview-active');
					$el.find('> .toggle-content').slideDown(self.options.duration);
				}

				self.events($el);
			});

			if(self.options.isAccordion) {
				self.options.duration = self.options.duration/2;
			}

			return this;
		},

		events: function($el) {
			var self = this,
				previewParCurrentHeight = 0,
				previewParAnimateHeight = 0,
				toggleContent = null;

			$el.find('> label').click(function(e) {

				var $this = $(this),
					parentSection = $this.parent(),
					parentWrapper = $this.parents('.toggle'),
					previewPar = null,
					closeElement = null;

				if(self.options.isAccordion && typeof(e.originalEvent) != 'undefined') {
					closeElement = parentWrapper.find('.toggle.active > label');

					if(closeElement[0] == $this[0]) {
						return;
					}
				}

				parentSection.toggleClass('active');

				// Preview Paragraph
				if(parentSection.find('> p').get(0)) {

					previewPar = parentSection.find('> p');
					previewParCurrentHeight = previewPar.css('height');
					previewPar.css('height', 'auto');
					previewParAnimateHeight = previewPar.css('height');
					previewPar.css('height', previewParCurrentHeight);

				}

				// Content
				toggleContent = parentSection.find('> .toggle-content');

				if(parentSection.hasClass('active')) {

					$(previewPar).animate({
						height: previewParAnimateHeight
					}, self.options.duration, function() {
						$(this).addClass('preview-active');
					});

					toggleContent.slideDown(self.options.duration, function() {
						if(closeElement) {
							closeElement.trigger('click');
						}
					});

				} else {

					$(previewPar).animate({
						height: 0
					}, self.options.duration, function() {
						$(this).removeClass('preview-active');
					});

					toggleContent.slideUp(self.options.duration);

				}

			});
		}
	};

	// expose to scope
	$.extend(theme, {
		PluginToggle: PluginToggle
	});

	// jquery plugin
	$.fn.themePluginToggle = function(opts) {
		return this.map(function() {
			var $this = $(this);

			if ($this.data(instanceName)) {
				return $this.data(instanceName);
			} else {
				return new PluginToggle($this, opts);
			}

		});
	}

}).apply(this, [ window.theme, jQuery ]);

// Widget - Todo
(function(theme, $) {

	theme = theme || {};

	var instanceName = '__widgetTodoList';

	var WidgetTodoList = function($el, opts) {
		return this.initialize($el, opts);
	};

	WidgetTodoList.defaults = {
	};

	WidgetTodoList.prototype = {
		initialize: function($el, opts) {
			if ( $el.data( instanceName ) ) {
				return this;
			}

			this.$el = $el;

			this
				.setData()
				.setOptions(opts)
				.build()
				.events();

			return this;
		},

		setData: function() {
			this.$el.data(instanceName, this);

			return this;
		},

		setOptions: function(opts) {
			this.options = $.extend( true, {}, WidgetTodoList.defaults, opts );

			return this;
		},

		check: function( input, label ) {
			if ( input.is(':checked') ) {
				label.addClass('line-through');
			} else {
				label.removeClass('line-through');
			}
		},

		build: function() {
			var _self = this,
				$check = this.$el.find('.todo-check');

			$check.each(function () {
				var label = $(this).closest('li').find('.todo-label');
				_self.check( $(this), label );
			});

			return this;
		},

		events: function() {
			var _self = this,
				$remove = this.$el.find( '.todo-remove' ),
				$check = this.$el.find('.todo-check'),
				$window = $( window );

			$remove.on('click.widget-todo-list', function( ev ) {
				ev.preventDefault();
				$(this).closest("li").remove();
			});

			$check.on('change', function () {
				var label = $(this).closest('li').find('.todo-label');
				_self.check( $(this), label );
			});

			if ( $.isFunction( $.fn.sortable ) ) {
				this.$el.sortable({
					sort: function(event, ui) {
						var top = event.pageY - _self.$el.offset().top - (ui.helper.outerHeight(true) / 2);
						ui.helper.css({'top' : top + 'px'});
				    }
				});
			}

			return this;
		}
	};

	// expose to scope
	$.extend(theme, {
		WidgetTodoList: WidgetTodoList
	});

	// jquery plugin
	$.fn.themePluginWidgetTodoList = function(opts) {
		return this.each(function() {
			var $this = $(this);

			if ($this.data(instanceName)) {
				return $this.data(instanceName);
			} else {
				return new WidgetTodoList($this, opts);
			}

		});
	}

}).apply(this, [ window.theme, jQuery ]);

// Widget - Toggle
(function(theme, $) {

	theme = theme || {};

	var instanceName = '__widgetToggleExpand';

	var WidgetToggleExpand = function($el, opts) {
		return this.initialize($el, opts);
	};

	WidgetToggleExpand.defaults = {
	};

	WidgetToggleExpand.prototype = {
		initialize: function($el, opts) {
			if ( $el.data( instanceName ) ) {
				return this;
			}

			this.$el = $el;

			this
				.setData()
				.setOptions(opts)
				.build()
				.events();

			return this;
		},

		setData: function() {
			this.$el.data(instanceName, this);

			return this;
		},

		setOptions: function(opts) {
			this.options = $.extend( true, {}, WidgetToggleExpand.defaults, opts );

			return this;
		},

		build: function() {
			return this;
		},

		events: function() {
			var _self = this,
				$toggler = this.$el.find( '.widget-toggle' );

			$toggler.on('click.widget-toggler', function() {
				_self.$el.hasClass('widget-collapsed') ? _self.expand( _self.$el ) : _self.collapse( _self.$el );
			});

			return this;
		},

		expand: function( content ) {
			content.children( '.widget-content-expanded' ).slideDown( 'fast', function() {
				$(this).css( 'display', '' );
				content.removeClass( 'widget-collapsed' );
			});
		},

		collapse: function( content ) {
			content.children('.widget-content-expanded' ).slideUp( 'fast', function() {
				content.addClass( 'widget-collapsed' );
				$(this).css( 'display', '' );
			});
		}
	};

	// expose to scope
	$.extend(theme, {
		WidgetToggleExpand: WidgetToggleExpand
	});

	// jquery plugin
	$.fn.themePluginWidgetToggleExpand = function(opts) {
		return this.each(function() {
			var $this = $(this);

			if ($this.data(instanceName)) {
				return $this.data(instanceName);
			} else {
				return new WidgetToggleExpand($this, opts);
			}

		});
	}

}).apply(this, [ window.theme, jQuery ]);

// Word Rotate
(function(theme, $) {

	theme = theme || {};

	var instanceName = '__wordRotate';

	var PluginWordRotate = function($el, opts) {
		return this.initialize($el, opts);
	};

	PluginWordRotate.defaults = {
		delay: 2000
	};

	PluginWordRotate.prototype = {
		initialize: function($el, opts) {
			if ( $el.data( instanceName ) ) {
				return this;
			}

			this.$el = $el;

			this
				.setData()
				.setOptions(opts)
				.build();

			return this;
		},

		setData: function() {
			this.$el.data(instanceName, this);

			return this;
		},

		setOptions: function(opts) {
			this.options = $.extend(true, {}, PluginWordRotate.defaults, opts, {
				wrapper: this.$el
			});

			return this;
		},

		build: function() {
			var $el = this.options.wrapper,
				itemsWrapper = $el.find(".word-rotate-items"),
				items = itemsWrapper.find("> span"),
				firstItem = items.eq(0),
				firstItemClone = firstItem.clone(),
				itemHeight = firstItem.height(),
				currentItem = 1,
				currentTop = 0;

			itemsWrapper.append(firstItemClone);

			$el
				.height(itemHeight)
				.addClass("active");

			setInterval(function() {

				currentTop = (currentItem * itemHeight);

				itemsWrapper.animate({
					top: -(currentTop) + "px"
				}, 300, function() {

					currentItem++;

					if(currentItem > items.length) {

						itemsWrapper.css("top", 0);
						currentItem = 1;

					}

				});

			}, this.options.delay);

			return this;
		}
	};

	// expose to scope
	$.extend(theme, {
		PluginWordRotate: PluginWordRotate
	});

	// jquery plugin
	$.fn.themePluginWordRotate = function(opts) {
		return this.each(function() {
			var $this = $(this);

			if ($this.data(instanceName)) {
				return $this.data(instanceName);
			} else {
				return new PluginWordRotate($this, opts);
			}

		});
	}

}).apply(this, [ window.theme, jQuery ]);

// Data Tables - Config
(function($) {

	'use strict';

	// we overwrite initialize of all datatables here
	// because we want to use select2, give search input a bootstrap look
	// keep in mind if you overwrite this fnInitComplete somewhere,
	// you should run the code inside this function to keep functionality.
	//
	// there's no better way to do this at this time :(
	if ( $.isFunction( $.fn[ 'dataTable' ] ) ) {

		$.extend(true, $.fn.dataTable.defaults, {
			sDom: "<'row datatables-header form-inline'<'col-sm-12 col-md-6'l><'col-sm-12 col-md-6'f>r><'table-responsive't><'row datatables-footer'<'col-sm-12 col-md-6'i><'col-sm-12 col-md-6'p>>",
			oLanguage: {
				sLengthMenu: '_MENU_ records per page',
				sProcessing: ' Loading'
			},
			fnInitComplete: function( settings, json ) {
				// select 2
				if ( $.isFunction( $.fn[ 'select2' ] ) ) {
					$('.dataTables_length select', settings.nTableWrapper).select2({
						minimumResultsForSearch: -1
					});
				}

				var options = $( 'table', settings.nTableWrapper ).data( 'plugin-options' ) || {};

				// search
				var $search = $('.dataTables_filter input', settings.nTableWrapper);

				$search
					.attr({
						placeholder: typeof options.searchPlaceholder !== 'undefined' ? options.searchPlaceholder : 'Search'
					})
					.addClass('form-control');

				if ( $.isFunction( $.fn.placeholder ) ) {
					$search.placeholder();
				}
			}
		});

	}

}).apply( this, [ jQuery ]);

// Notifications - Config
(function($) {

	'use strict';

	// use font awesome icons if available
	if ( typeof PNotify != 'undefined' ) {
		PNotify.prototype.options.styling = "fontawesome";

		$.extend(true, PNotify.prototype.options, {
			shadow: false,
			stack: {
				spacing1: 15,
	        	spacing2: 15
        	}
		});

		$.extend(PNotify.styling.fontawesome, {
			// classes
			container: "notification",
			notice: "notification-warning",
			info: "notification-info",
			success: "notification-success",
			error: "notification-danger",

			// icons
			notice_icon: "fa fa-exclamation",
			info_icon: "fa fa-info",
			success_icon: "fa fa-check",
			error_icon: "fa fa-times"
		});
	}

}).apply(this, [jQuery]);


text:
package com.github.sbugat.nqueens.solvers.bruteforce.instrumentations;

import com.github.sbugat.nqueens.GenericInstrumentedNQueensSolver;

/**
 * Brute-force algorithm for the N queens puzzle solver.
 *
 * @author Sylvain Bugat
 *
 */
public final class BruteForceNQueensSolverGridConstraints extends GenericInstrumentedNQueensSolver {

	/** Chessboard with only one dimension with all lines. */
	private boolean[] chessboard;
	/** Array to count queens on each column. */
	private int[] columnCounts;
	/** Array to count queens on each line. */
	private int[] lineCounts;
	/** Array to count queens on ascending diagonals, diagonal number = x + y. */
	private int[] ascendingDiagonalCounts;
	/** Array to count queens on descending diagonals, diagonal number = x + chessboard size - 1 - y. */
	private int[] descendingDiagonalCounts;

	/** Current number of placedQueens */
	private int placedQueens;

	public BruteForceNQueensSolverGridConstraints(final int chessboardSizeArg, final boolean printSolutionArg) {

		super(chessboardSizeArg, printSolutionArg);

		chessboard = new boolean[chessboardSizeArg * chessboardSizeArg];
		columnCounts = new int[chessboardSizeArg];
		lineCounts = new int[chessboardSizeArg];
		ascendingDiagonalCounts = new int[chessboardSizeArg * 2 - 1];
		descendingDiagonalCounts = new int[chessboardSizeArg * 2 - 1];
	}

	@Override
	public long solve() {

		// Start the algorithm at the first position
		methodCallsCount++;
		solve(0);

		// Return the number of solutions found
		return solutionCount;
	}

	/**
	 * Solving recursive method, do a brute-force algorithm by testing all combinations.
	 *
	 * @param position current one-dimention position starting at 0
	 */
	private void solve(final int position) {

		// Recalculate X and Y coordinates
		final int y = position / chessboardSize;
		final int x = position % chessboardSize;

		// Put a queen on the current position
		squareWritesCount++;
		chessboard[position] = true;
		lineCounts[y]++;
		columnCounts[x]++;
		final int ascendingDiagonal = x + y;
		final int descendingDiagonal = x + chessboardSize - 1 - y;
		ascendingDiagonalCounts[ascendingDiagonal]++;
		descendingDiagonalCounts[descendingDiagonal]++;
		placedQueens++;

		// All queens are sets then a solution may be present
		explicitTestsCount++;
		if (placedQueens >= chessboardSize) {

			methodCallsCount++;
			explicitTestsCount++;
			if (checkSolutionChessboard()) {

				solutionCount++;
				methodCallsCount++;
				print();
			}
		}
		else {

			// End of the chessboard check
			if (position + 1 < chessboard.length) {
				methodCallsCount++;
				solve(position + 1);
			}
		}

		// Remove the queen on the current position
		placedQueens--;
		ascendingDiagonalCounts[ascendingDiagonal]--;
		descendingDiagonalCounts[descendingDiagonal]--;
		lineCounts[y]--;
		columnCounts[x]--;
		squareWritesCount++;
		chessboard[position] = false;

		// End of the chessboard check
		explicitTestsCount++;
		if (position + 1 < chessboard.length) {
			methodCallsCount++;
			solve(position + 1);
		}
	}

	/**
	 * Check if a chessboard with N queens is a solution (only one queens per lines, columns and diagnonals).
	 *
	 * @return true if the chessboard contain a solution, false otherwise
	 */
	private boolean checkSolutionChessboard() {

		// Check if 2 queens are on the same line and column
		implicitTestsCount++;
		for (int i = 0; i < columnCounts.length; i++) {

			explicitTestsCount++;
			if (columnCounts[i] > 1) {
				return false;
			}
			else {
				explicitTestsCount++;
				if (lineCounts[i] > 1) {
					return false;
				}
			}

			implicitTestsCount++;
		}

		// Check if 2 queens are on the same column
		implicitTestsCount++;
		for (int i = 0; i < ascendingDiagonalCounts.length; i++) {

			explicitTestsCount++;
			if (ascendingDiagonalCounts[i] > 1) {
				return false;
			}
			else {
				explicitTestsCount++;
				if (descendingDiagonalCounts[i] > 1) {
					return false;
				}
			}

			implicitTestsCount++;
		}

		return true;
	}

	@Override
	public void reset() {

		super.reset();

		final int chessboardArrayLength = chessboardSize * chessboardSize;
		if (chessboard.length != chessboardArrayLength) {
			chessboard = new boolean[chessboardArrayLength];
			columnCounts = new int[chessboardSize];
			lineCounts = new int[chessboardSize];
			ascendingDiagonalCounts = new int[chessboardSize * 2 - 1];
			descendingDiagonalCounts = new int[chessboardSize * 2 - 1];
		}
	}

	@Override
	public boolean getChessboardPosition(final int x, final int y) {
		return chessboard[y * chessboardSize + x];
	}

	@Override
	public String getName() {
		return "Grid constraints array brute-force";
	}
}

redpajama_set_name: RedPajamaGithub