diff --git a/Fetmailalias, b/Fetmailalias, new file mode 100644 index 0000000..e69de29 diff --git a/Gemfile b/Gemfile index 486db40..39c0935 100755 --- a/Gemfile +++ b/Gemfile @@ -13,8 +13,8 @@ gem 'webrick', '1.3.1' # Gems used only for assets and not required # in production environments by default. - gem 'sass-rails', '~> 3.2' - gem 'coffee-rails', '~> 3.2.1' + gem 'sass-rails', '~> 3.0' + gem 'coffee-rails', '~> 3.0' gem 'bootstrap-sass','~> 2.3.2.1' group :assets do @@ -60,16 +60,17 @@ gem "paper_trail" , '~>3.0.5'#, :git=>'git://github.com/airblade/paper_trail.git # User management gem "devise" ,'~>2.2.3' -gem "omniauth" -gem "omniauth-facebook" -gem "omniauth-ldap" +gem "omniauth", '~>1.2.2' +gem "omniauth-facebook", '~>2.0.0' +gem "omniauth-ldap", '~>1.0.4' -gem "fb_graph" +gem "fb_graph",'~>2.7.17' gem "meta-tags" # Roles for users -gem "rolify" +gem "rolify", '~>4.0.0' # Abilities -gem "cancan" +gem "cancan", '~>1.6.0' + # Fileupload @@ -84,8 +85,16 @@ gem 'awesome_nested_set' gem 'annotate', ">=2.5.0" gem 'carrierwave', "~>0.9.0" group :development, :test do -gem 'factory_girl_rails' -gem 'rspec-rails' +gem 'factory_girl_rails',"~>4.5.0" +gem 'minitest', "~>4.7.5" +gem 'progress_bar' +gem 'rspec-core', '~>3.3.0' +gem 'rspec-rails', '~>3.3.0' +gem 'rspec-collection_matchers' +gem 'rspec-activemodel-mocks' +gem 'rspec-html-matchers' +gem 'rspec-expectations' +gem 'capybara', '~>2.5.0' end gem "simple_calendar", "~> 0.1.9" @@ -93,7 +102,7 @@ gem 'rmagick' gem 'bootstrap-addons-rails' gem "jquery-fileupload-rails", "0.4.1" -gem "jquery-ui-rails","~> 4.1.1" +gem "jquery-ui-rails","~> 4.1.0" gem "font-awesome-rails" gem "jquery-datetimepicker-rails" # gem "jquery-sortable-rails" @@ -124,4 +133,6 @@ gem 'opengraph_parser' gem 'blueimp-gallery' gem 'blueimp-gallery-rails' -gem 'shareable' \ No newline at end of file +gem 'shareable' + +gem 'sunspot_test' \ No newline at end of file diff --git a/MyText, b/MyText, new file mode 100644 index 0000000..e69de29 diff --git a/Nachname, b/Nachname, new file mode 100644 index 0000000..e69de29 diff --git a/Short, b/Short, new file mode 100644 index 0000000..e69de29 diff --git a/Vorname, b/Vorname, new file mode 100644 index 0000000..e69de29 diff --git a/app/assets/javascripts/jmpress.js b/app/assets/javascripts/jmpress.js new file mode 100644 index 0000000..39f5624 --- /dev/null +++ b/app/assets/javascripts/jmpress.js @@ -0,0 +1,2659 @@ +/*! + * jmpress.js v0.4.5 + * http://jmpressjs.github.com/jmpress.js + * + * A jQuery plugin to build a website on the infinite canvas. + * + * Copyright 115 Kyle Robinson Young @shama & Tobias Koppers @sokra + * Licensed MIT + * http://www.opensource.org/licenses/mit-license.php + * + * Based on the foundation laid by Bartek Szopka @bartaz + */ +/* + * core.js + * The core of jmpress.js + */ +(function( $, document, window, undefined ) { + + 'use strict'; + + /** + * Set supported prefixes + * + * @access protected + * @return Function to get prefixed property + */ + var pfx = (function () { + var style = document.createElement('dummy').style, + prefixes = 'Webkit Moz O ms Khtml'.split(' '), + memory = {}; + return function ( prop ) { + if ( typeof memory[ prop ] === "undefined" ) { + var ucProp = prop.charAt(0).toUpperCase() + prop.substr(1), + props = (prop + ' ' + prefixes.join(ucProp + ' ') + ucProp).split(' '); + memory[ prop ] = null; + for ( var i in props ) { + if ( style[ props[i] ] !== undefined ) { + memory[ prop ] = props[i]; + break; + } + } + } + return memory[ prop ]; + }; + }()); + + /** + * map ex. "WebkitTransform" to "-webkit-transform" + */ + function mapProperty( name ) { + if(!name) { + return; + } + var index = 1 + name.substr(1).search(/[A-Z]/); + var prefix = name.substr(0, index).toLowerCase(); + var postfix = name.substr(index).toLowerCase(); + return "-" + prefix + "-" + postfix; + } + function addComma( attribute ) { + if(!attribute) { + return ""; + } + return attribute + ","; + } + /** + * Return an jquery object only if it's not empty + */ + function ifNotEmpty(el) { + if(el.length > 0) { + return el; + } + return null; + } + + /** + * Default Settings + */ + var defaults = { + /* CLASSES */ + stepSelector: '.step' + ,containerClass: '' + ,canvasClass: '' + ,areaClass: '' + ,notSupportedClass: 'not-supported' + + /* CONFIG */ + ,fullscreen: true + + /* ANIMATION */ + ,animation: { + transformOrigin: 'top left' + ,transitionProperty: addComma(mapProperty(pfx('transform'))) + addComma(mapProperty(pfx('perspective'))) + 'opacity' + ,transitionDuration: '1s' + ,transitionDelay: '500ms' + ,transitionTimingFunction: 'ease-in-out' + ,transformStyle: "preserve-3d" + } + ,transitionDuration: 1500 + }; + var callbacks = { + 'beforeChange': 1 + ,'beforeInitStep': 1 + ,'initStep': 1 + ,'checkNoSupport': 1 + ,'beforeInit': 1 + ,'afterInit': 1 + ,'beforeDeinit': 1 + ,'afterDeinit': 1 + ,'applyStep': 1 + ,'unapplyStep': 1 + ,'setInactive': 1 + ,'beforeActive': 1 + ,'setActive': 1 + ,'selectInitialStep': 1 + ,'selectPrev': 1 + ,'selectNext': 1 + ,'selectHome': 1 + ,'selectEnd': 1 + ,'idle': 1 + ,'applyTarget': 1 + }; + for(var callbackName in callbacks) { + defaults[callbackName] = []; + } + + + /** + * Initialize jmpress + */ + function init( args ) { + args = $.extend(true, {}, args || {}); + + // accept functions and arrays of functions as callbacks + var callbackArgs = {}; + var callbackName = null; + for (callbackName in callbacks) { + callbackArgs[callbackName] = $.isFunction( args[callbackName] ) ? + [ args[callbackName] ] : + args[callbackName]; + args[callbackName] = []; + } + + // MERGE SETTINGS + var settings = $.extend(true, {}, defaults, args); + + for (callbackName in callbacks) { + if (callbackArgs[callbackName]) { + Array.prototype.push.apply(settings[callbackName], callbackArgs[callbackName]); + } + } + + /*** MEMBER VARS ***/ + + var jmpress = $( this ) + ,container = null + ,area = null + ,oldStyle = { + container: "" + ,area: "" + } + ,canvas = null + ,current = null + ,active = false + ,activeSubstep = null + ,activeDelegated = false; + + + /*** MEMBER FUNCTIONS ***/ + // functions have to be called with this + + /** + * Init a single step + * + * @param element the element of the step + * @param idx number of step + */ + function doStepInit( element, idx ) { + var data = dataset( element ); + var step = { + oldStyle: $(element).attr("style") || "" + }; + + var callbackData = { + data: data + ,stepData: step + }; + callCallback.call(this, 'beforeInitStep', $(element), callbackData); + step.delegate = data.delegate; + callCallback.call(this, 'initStep', $(element), callbackData); + + $(element).data('stepData', step); + + if ( !$(element).attr('id') ) { + $(element).attr('id', 'step-' + (idx + 1)); + } + + callCallback.call(this, 'applyStep', $(element), callbackData); + } + /** + * Deinit a single step + * + * @param element the element of the step + */ + function doStepDeinit( element ) { + var stepData = $(element).data('stepData'); + + $(element).attr("style", stepData.oldStyle); + + callCallback.call(this, 'unapplyStep', $(element), { + stepData: stepData + }); + } + /** + * Reapplies stepData to the element + * + * @param element + */ + function doStepReapply( element ) { + callCallback.call(this, 'unapplyStep', $(element), { + stepData: element.data("stepData") + }); + + callCallback.call(this, 'applyStep', $(element), { + stepData: element.data("stepData") + }); + } + /** + * Completly deinit jmpress + * + */ + function deinit() { + if ( active ) { + callCallback.call(this, 'setInactive', active, { + stepData: $(active).data('stepData') + ,reason: "deinit" + } ); + } + if (current.jmpressClass) { + $(jmpress).removeClass(current.jmpressClass); + } + + callCallback.call(this, 'beforeDeinit', $(this), {}); + + $(settings.stepSelector, jmpress).each(function( idx ) { + doStepDeinit.call(jmpress, this ); + }); + + container.attr("style", oldStyle.container); + if(settings.fullscreen) { + $("html").attr("style", ""); + } + area.attr("style", oldStyle.area); + $(canvas).children().each(function() { + jmpress.append( $( this ) ); + }); + if( settings.fullscreen ) { + canvas.remove(); + } else { + canvas.remove(); + area.remove(); + } + + callCallback.call(this, 'afterDeinit', $(this), {}); + + $(jmpress).data("jmpressmethods", false); + + if(current.idleTimeout) { + clearTimeout(current.idleTimeout); + } + } + /** + * Call a callback + * + * @param callbackName String callback which should be called + * @param element some arguments to the callback + * @param eventData + */ + function callCallback( callbackName, element, eventData ) { + eventData.settings = settings; + eventData.current = current; + eventData.container = container; + eventData.parents = element ? getStepParents(element) : null; + eventData.current = current; + eventData.jmpress = this; + var result = {}; + $.each( settings[callbackName], function(idx, callback) { + result.value = callback.call( jmpress, element, eventData ) || result.value; + }); + return result.value; + } + /** + * + */ + function getStepParents( el ) { + return $(el).parentsUntil(jmpress).not(jmpress).filter(settings.stepSelector); + } + /** + * Reselect the active step + * + * @param String type reason of reselecting step + */ + function reselect( type ) { + return select( { step: active, substep: activeSubstep }, type); + } + /** + * Select a given step + * + * @param el element to select + * @param type reason of changing step + * @return Object element selected + */ + function select( el, type ) { + var substep; + if ( $.isPlainObject( el ) ) { + substep = el.substep; + el = el.step; + } + if ( typeof el === 'string') { + el = jmpress.find( el ).first(); + } + if ( !el || !$(el).data('stepData') ) { + return false; + } + + scrollFix.call(this); + + var step = $(el).data('stepData'); + + var cancelSelect = false; + callCallback.call(this, "beforeChange", el, { + stepData: step + ,reason: type + ,cancel: function() { + cancelSelect = true; + } + }); + if (cancelSelect) { + return undefined; + } + + var target = {}; + + var delegated = el; + if($(el).data("stepData").delegate) { + delegated = ifNotEmpty($(el).parentsUntil(jmpress).filter(settings.stepSelector).filter(step.delegate)) || + ifNotEmpty($(el).near(step.delegate)) || + ifNotEmpty($(el).near(step.delegate, true)) || + ifNotEmpty($(step.delegate, jmpress)); + if(delegated) { + step = delegated.data("stepData"); + } else { + // Do not delegate if expression not found + delegated = el; + } + } + if ( activeDelegated ) { + callCallback.call(this, 'setInactive', activeDelegated, { + stepData: $(activeDelegated).data('stepData') + ,delegatedFrom: active + ,reason: type + ,target: target + ,nextStep: delegated + ,nextSubstep: substep + ,nextStepData: step + } ); + } + var callbackData = { + stepData: step + ,delegatedFrom: el + ,reason: type + ,target: target + ,substep: substep + ,prevStep: activeDelegated + ,prevSubstep: activeSubstep + ,prevStepData: activeDelegated && $(activeDelegated).data('stepData') + }; + callCallback.call(this, 'beforeActive', delegated, callbackData); + callCallback.call(this, 'setActive', delegated, callbackData); + + // Set on step class on root element + if (current.jmpressClass) { + $(jmpress).removeClass(current.jmpressClass); + } + $(jmpress).addClass(current.jmpressClass = 'step-' + $(delegated).attr('id') ); + if (current.jmpressDelegatedClass) { + $(jmpress).removeClass(current.jmpressDelegatedClass); + } + $(jmpress).addClass(current.jmpressDelegatedClass = 'delegating-step-' + $(el).attr('id') ); + + callCallback.call(this, "applyTarget", delegated, $.extend({ + canvas: canvas + ,area: area + ,beforeActive: activeDelegated + }, callbackData)); + + active = el; + activeSubstep = callbackData.substep; + activeDelegated = delegated; + + if(current.idleTimeout) { + clearTimeout(current.idleTimeout); + } + current.idleTimeout = setTimeout(function() { + callCallback.call(this, 'idle', delegated, callbackData); + }, Math.max(1, settings.transitionDuration - 100)); + + return delegated; + } + /** + * This should fix ANY kind of buggy scrolling + */ + function scrollFix() { + (function fix() { + if ($(container)[0].tagName === "BODY") { + try { + window.scrollTo(0, 0); + } catch(e) {} + } + $(container).scrollTop(0); + $(container).scrollLeft(0); + function check() { + if ($(container).scrollTop() !== 0 || + $(container).scrollLeft() !== 0) { + fix(); + } + } + setTimeout(check, 1); + setTimeout(check, 10); + setTimeout(check, 100); + setTimeout(check, 200); + setTimeout(check, 400); + }()); + } + /** + * Alias for select + */ + function goTo( el ) { + return select.call(this, el, "jump" ); + } + /** + * Goto Next Slide + * + * @return Object newly active slide + */ + function next() { + return select.call(this, callCallback.call(this, 'selectNext', active, { + stepData: $(active).data('stepData') + ,substep: activeSubstep + }), "next" ); + } + /** + * Goto Previous Slide + * + * @return Object newly active slide + */ + function prev() { + return select.call(this, callCallback.call(this, 'selectPrev', active, { + stepData: $(active).data('stepData') + ,substep: activeSubstep + }), "prev" ); + } + /** + * Goto First Slide + * + * @return Object newly active slide + */ + function home() { + return select.call(this, callCallback.call(this, 'selectHome', active, { + stepData: $(active).data('stepData') + }), "home" ); + } + /** + * Goto Last Slide + * + * @return Object newly active slide + */ + function end() { + return select.call(this, callCallback.call(this, 'selectEnd', active, { + stepData: $(active).data('stepData') + }), "end" ); + } + /** + * Manipulate the canvas + * + * @param props + * @return Object + */ + function canvasMod( props ) { + css(canvas, props || {}); + return $(canvas); + } + /** + * Return current step + * + * @return Object + */ + function getActive() { + return activeDelegated && $(activeDelegated); + } + /** + * fire a callback + * + * @param callbackName + * @param element + * @param eventData + * @return void + */ + function fire( callbackName, element, eventData ) { + if( !callbacks[callbackName] ) { + $.error( "callback " + callbackName + " is not registered." ); + } else { + return callCallback.call(this, callbackName, element, eventData); + } + } + + /** + * PUBLIC METHODS LIST + */ + jmpress.data("jmpressmethods", { + select: select + ,reselect: reselect + ,scrollFix: scrollFix + ,goTo: goTo + ,next: next + ,prev: prev + ,home: home + ,end: end + ,canvas: canvasMod + ,container: function() { return container; } + ,settings: function() { return settings; } + ,active: getActive + ,current: function() { return current; } + ,fire: fire + ,init: function(step) { + doStepInit.call(this, $(step), current.nextIdNumber++); + } + ,deinit: function(step) { + if(step) { + doStepDeinit.call(this, $(step)); + } else { + deinit.call(this); + } + } + ,reapply: doStepReapply + }); + + /** + * Check for support + * This will be removed in near future, when support is coming + * + * @access protected + * @return void + */ + function checkSupport() { + var ua = navigator.userAgent.toLowerCase(); + return (ua.search(/(iphone)|(ipod)|(android)/) === -1) || (ua.search(/(chrome)/) !== -1); + } + + // BEGIN INIT + + // CHECK FOR SUPPORT + if (checkSupport() === false || callCallback.call(this, 'checkNoSupport', null, {})) { + // not supported + if (settings.notSupportedClass) { + jmpress.addClass(settings.notSupportedClass); + } + return; + } else { + if (settings.notSupportedClass) { + jmpress.removeClass(settings.notSupportedClass); + } + } + + // grabbing all steps + var steps = $(settings.stepSelector, jmpress); + + // GERNERAL INIT OF FRAME + container = jmpress; + area = $('
'); + canvas = $('
'); + $(jmpress).children().filter(steps).each(function() { + canvas.append( $( this ) ); + }); + if(settings.fullscreen) { + container = $('body'); + $("html").css({ + overflow: 'hidden' + }); + area = jmpress; + } + oldStyle.area = area.attr("style") || ""; + oldStyle.container = container.attr("style") || ""; + if(settings.fullscreen) { + container.css({ + height: '100%' + }); + jmpress.append( canvas ); + } else { + container.css({ + position: "relative" + }); + area.append( canvas ); + jmpress.append( area ); + } + + $(container).addClass(settings.containerClass); + $(area).addClass(settings.areaClass); + $(canvas).addClass(settings.canvasClass); + + document.documentElement.style.height = "100%"; + container.css({ + overflow: 'hidden' + }); + + var props = { + position: "absolute" + ,transitionDuration: '0s' + }; + props = $.extend({}, settings.animation, props); + css(area, props); + css(canvas, props); + + current = {}; + + callCallback.call(this, 'beforeInit', null, { + canvas: canvas, + area: area + }); + + // INITIALIZE EACH STEP + steps.each(function( idx ) { + doStepInit.call(jmpress, this, idx ); + }); + current.nextIdNumber = steps.length; + + callCallback.call(this, 'afterInit', null, {}); + + // START + select.call(this, callCallback.call(this, 'selectInitialStep', "init", {}) ); + + if (settings.initClass) { + $(steps).removeClass(settings.initClass); + } + } + /** + * Return default settings + * + * @return Object + */ + function getDefaults() { + return defaults; + } + /** + * Register a callback or a jmpress function + * + * @access public + * @param name String the name of the callback or function + * @param func Function? the function to be added + */ + function register(name, func) { + if( $.isFunction(func) ) { + if( methods[name] ) { + $.error( "function " + name + " is already registered." ); + } else { + methods[name] = func; + } + } else { + if( callbacks[name] ) { + $.error( "callback " + name + " is already registered." ); + } else { + callbacks[name] = 1; + defaults[name] = []; + } + } + } + /** + * Set CSS on element w/ prefixes + * + * @return Object element which properties were set + * + * TODO: Consider bypassing pfx and blindly set as jQuery + * already checks for support + */ + function css( el, props ) { + var key, pkey, cssObj = {}; + for ( key in props ) { + if ( props.hasOwnProperty(key) ) { + pkey = pfx(key); + if ( pkey !== null ) { + cssObj[pkey] = props[key]; + } + } + } + $(el).css(cssObj); + return el; + } + /** + * Return dataset for element + * + * @param el element + * @return Object + */ + function dataset( el ) { + if ( $(el)[0].dataset ) { + return $.extend({}, $(el)[0].dataset); + } + function toCamelcase( str ) { + str = str.split( '-' ); + for( var i = 1; i < str.length; i++ ) { + str[i] = str[i].substr(0, 1).toUpperCase() + str[i].substr(1); + } + return str.join( '' ); + } + var returnDataset = {}; + var attrs = $(el)[0].attributes; + $.each(attrs, function ( idx, attr ) { + if ( attr.nodeName.substr(0, 5) === "data-" ) { + returnDataset[ toCamelcase(attr.nodeName.substr(5)) ] = attr.nodeValue; + } + }); + return returnDataset; + } + /** + * Returns true, if jmpress is initialized + * + * @return bool + */ + function initialized() { + return !!$(this).data("jmpressmethods"); + } + + + /** + * PUBLIC STATIC METHODS LIST + */ + var methods = { + init: init + ,initialized: initialized + ,deinit: function() {} + ,css: css + ,pfx: pfx + ,defaults: getDefaults + ,register: register + ,dataset: dataset + }; + + /** + * $.jmpress() + */ + $.fn.jmpress = function( method ) { + function f() { + var jmpressmethods = $(this).data("jmpressmethods"); + if ( jmpressmethods && jmpressmethods[method] ) { + return jmpressmethods[method].apply( this, Array.prototype.slice.call( arguments, 1 )); + } else if ( methods[method] ) { + return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 )); + } else if ( callbacks[method] && jmpressmethods ) { + var settings = jmpressmethods.settings(); + var func = Array.prototype.slice.call( arguments, 1 )[0]; + if ($.isFunction( func )) { + settings[method] = settings[method] || []; + settings[method].push(func); + } + } else if ( typeof method === 'object' || ! method ) { + return init.apply( this, arguments ); + } else { + $.error( 'Method ' + method + ' does not exist on jQuery.jmpress' ); + } + // to allow chaining + return this; + } + var args = arguments; + var result; + $(this).each(function(idx, element) { + result = f.apply(element, args); + }); + return result; + }; + $.extend({ + jmpress: function( method ) { + if ( methods[method] ) { + return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 )); + } else if ( callbacks[method] ) { + // plugin interface + var func = Array.prototype.slice.call( arguments, 1 )[0]; + if ($.isFunction( func )) { + defaults[method].push(func); + } else { + $.error( 'Second parameter should be a function: $.jmpress( callbackName, callbackFunction )' ); + } + } else { + $.error( 'Method ' + method + ' does not exist on jQuery.jmpress' ); + } + } + }); + +}(jQuery, document, window)); + +/* + * near.js + * Find steps near each other + */ +(function( $, document, window, undefined ) { + + 'use strict'; + + // add near( selector, backwards = false) to jquery + + + function checkAndGo( elements, func, selector, backwards ) { + var next; + elements.each(function(idx, element) { + if(backwards) { + next = func(element, selector, backwards); + if (next) { + return false; + } + } + if( $(element).is(selector) ) { + next = element; + return false; + } + if(!backwards) { + next = func(element, selector, backwards); + if (next) { + return false; + } + } + }); + return next; + } + function findNextInChildren(item, selector, backwards) { + var children = $(item).children(); + if(backwards) { + children = $(children.get().reverse()); + } + return checkAndGo( children, findNextInChildren, selector, backwards ); + } + function findNextInSiblings(item, selector, backwards) { + return checkAndGo( + $(item)[backwards ? "prevAll" : "nextAll"](), + findNextInChildren, selector, backwards ); + } + function findNextInParents(item, selector, backwards) { + var next; + var parents = $(item).parents(); + parents = $(parents.get()); + $.each(parents.get(), function(idx, element) { + if( backwards && $(element).is(selector) ) { + next = element; + return false; + } + next = findNextInSiblings(element, selector, backwards); + if(next) { + return false; + } + }); + return next; + } + + $.fn.near = function( selector, backwards ) { + var array = []; + $(this).each(function(idx, element) { + var near = (backwards ? + false : + findNextInChildren( element, selector, backwards )) || + findNextInSiblings( element, selector, backwards ) || + findNextInParents( element, selector, backwards ); + if( near ) { + array.push(near); + } + }); + return $(array); + }; +}(jQuery, document, window)); +/* + * transform.js + * The engine that powers the transforms or falls back to other methods + */ +(function( $, document, window, undefined ) { + + 'use strict'; + + /* FUNCTIONS */ + function toCssNumber(number) { + return (Math.round(10000*number)/10000)+""; + } + + /** + * 3D and 2D engines + */ + var engines = { + 3: { + transform: function( el, data, settings ) { + var transform = 'translate(-' + settings.originX + ',-' + settings.originY + ')'; + $.each(data, function(idx, item) { + var coord = ["X", "Y", "Z"]; + var i; + if(item[0] === "translate") { // ["translate", x, y, z] + transform += " translate3d(" + toCssNumber(item[1] || 0) + "px," + toCssNumber(item[2] || 0) + "px," + toCssNumber(item[3] || 0) + "px)"; + } else if(item[0] === "rotate") { + var order = item[4] ? [1, 2, 3] : [3, 2, 1]; + for(i = 0; i < 3; i++) { + transform += " rotate" + coord[order[i]-1] + "(" + toCssNumber(item[order[i]] || 0) + "deg)"; + } + } else if(item[0] === "scale") { + for(i = 0; i < 3; i++) { + transform += " scale" + coord[i] + "(" + toCssNumber(item[i+1] || 1) + ")"; + } + } + }); + $.jmpress("css", el, $.extend({}, { transform: transform })); + } + } + ,2: { + transform: function( el, data, settings ) { + var transform = 'translate(-' + settings.originX + ',-' + settings.originY + ')'; + $.each(data, function(idx, item) { + var coord = ["X", "Y"]; + if(item[0] === "translate") { // ["translate", x, y, z] + transform += " translate(" + toCssNumber(item[1] || 0) + "px," + toCssNumber(item[2] || 0) + "px)"; + } else if(item[0] === "rotate") { + transform += " rotate(" + toCssNumber(item[3] || 0) + "deg)"; + } else if(item[0] === "scale") { + for(var i = 0; i < 2; i++) { + transform += " scale" + coord[i] + "(" + toCssNumber(item[i+1] || 1) + ")"; + } + } + }); + $.jmpress("css", el, $.extend({}, { transform: transform })); + } + } + }; + + /** + * Engine to power cross-browser translate, scale and rotate. + */ + var engine = (function() { + if ($.jmpress("pfx", "perspective")) { + return engines[3]; + } else if ($.jmpress("pfx", "transform")) { + return engines[2]; + } + }()); + + if(!engine) { + $.jmpress("checkNoSupport", function() { + return true; + }); + } + + var jmpressDefaults = $.jmpress("defaults"); + jmpressDefaults.reasonableAnimation = {}; + jmpressDefaults.originX = "50%"; + jmpressDefaults.originY = "50%"; + $.jmpress("initStep", function( step, eventData ) { + var data = eventData.data; + var stepData = eventData.stepData; + var pf = parseFloat; + $.extend(stepData, { + x: pf(data.x) || 0 + ,y: pf(data.y) || 0 + ,z: pf(data.z) || 0 + ,r: pf(data.r) || 0 + ,phi: pf(data.phi) || 0 + ,rotate: pf(data.rotate) || 0 + ,rotateX: pf(data.rotateX) || 0 + ,rotateY: pf(data.rotateY) || 0 + ,rotateZ: pf(data.rotateZ) || 0 + ,revertRotate: false + ,scale: pf(data.scale) || 1 + ,scaleX: pf(data.scaleX) || false + ,scaleY: pf(data.scaleY) || false + ,scaleZ: pf(data.scaleZ) || 1 + }); + }); + $.jmpress("beforeInit", function( nil, eventData ) { + $.jmpress("css", eventData.area, { + left: eventData.settings.originX, + top: eventData.settings.originY, + perspective: '1000px', + }); + }); + $.jmpress("afterInit", function( nil, eventData ) { + var stepSelector = eventData.settings.stepSelector, + current = eventData.current; + current.perspectiveScale = 1; + current.maxNestedDepth = 0; + var nestedSteps = $(eventData.jmpress).find(stepSelector).children(stepSelector); + while(nestedSteps.length) { + current.maxNestedDepth++; + nestedSteps = nestedSteps.children(stepSelector); + } + }); + $.jmpress("applyStep", function( step, eventData ) { + $.jmpress("css", $(step), { + position: "absolute" + ,transformStyle: "preserve-3d" + }); + if ( eventData.parents.length > 0 ) { + $.jmpress("css", $(step), { + top: "50%" + ,left: "50%" + }); + } + var sd = eventData.stepData; + var transform = [ + ["translate", + sd.x || (sd.r * Math.sin(sd.phi*Math.PI/180)), + sd.y || (-sd.r * Math.cos(sd.phi*Math.PI/180)), + sd.z], + ["rotate", + sd.rotateX, + sd.rotateY, + sd.rotateZ || sd.rotate, + true], + ["scale", + sd.scaleX || sd.scale, + sd.scaleY || sd.scale, + sd.scaleZ || sd.scale] + ]; + engine.transform( step, transform, eventData.settings ); + }); + $.jmpress("setActive", function( element, eventData ) { + var target = eventData.target; + var step = eventData.stepData; + var tf = target.transform = []; + target.perspectiveScale = 1; + + for(var i = eventData.current.maxNestedDepth; i > (eventData.parents.length || 0); i--) { + tf.push(["scale"], ["rotate"], ["translate"]); + } + + tf.push(["scale", + 1 / (step.scaleX || step.scale), + 1 / (step.scaleY || step.scale), + 1 / (step.scaleZ)]); + tf.push(["rotate", + -step.rotateX, + -step.rotateY, + -(step.rotateZ || step.rotate)]); + tf.push(["translate", + -(step.x || (step.r * Math.sin(step.phi*Math.PI/180))), + -(step.y || (-step.r * Math.cos(step.phi*Math.PI/180))), + -step.z]); + target.perspectiveScale *= (step.scaleX || step.scale); + + $.each(eventData.parents, function(idx, element) { + var step = $(element).data("stepData"); + tf.push(["scale", + 1 / (step.scaleX || step.scale), + 1 / (step.scaleY || step.scale), + 1 / (step.scaleZ)]); + tf.push(["rotate", + -step.rotateX, + -step.rotateY, + -(step.rotateZ || step.rotate)]); + tf.push(["translate", + -(step.x || (step.r * Math.sin(step.phi*Math.PI/180))), + -(step.y || (-step.r * Math.cos(step.phi*Math.PI/180))), + -step.z]); + target.perspectiveScale *= (step.scaleX || step.scale); + }); + + $.each(tf, function(idx, item) { + if(item[0] !== "rotate") { + return; + } + function lowRotate(name) { + if(eventData.current["rotate"+name+"-"+idx] === undefined) { + eventData.current["rotate"+name+"-"+idx] = item[name] || 0; + } + var cur = eventData.current["rotate"+name+"-"+idx], tar = item[name] || 0, + curmod = cur % 360, tarmod = tar % 360; + if(curmod < 0) { + curmod += 360; + } + if(tarmod < 0) { + tarmod += 360; + } + var diff = tarmod - curmod; + if(diff < -180) { + diff += 360; + } else if(diff > 180) { + diff -= 360; + } + eventData.current["rotate"+name+"-"+idx] = item[name] = cur + diff; + } + lowRotate(1); + lowRotate(2); + lowRotate(3); + }); + }); + $.jmpress("applyTarget", function( active, eventData ) { + + var target = eventData.target, + props, step = eventData.stepData, + settings = eventData.settings, + zoomin = target.perspectiveScale * 1.3 < eventData.current.perspectiveScale, + zoomout = target.perspectiveScale > eventData.current.perspectiveScale * 1.3; + + // extract first scale from transform + var lastScale = -1; + $.each(target.transform, function(idx, item) { + if(item.length <= 1) { + return; + } + if(item[0] === "rotate" && + item[1] % 360 === 0 && + item[2] % 360 === 0 && + item[3] % 360 === 0) { + return; + } + if(item[0] === "scale") { + lastScale = idx; + } else { + return false; + } + }); + + if(lastScale !== eventData.current.oldLastScale) { + zoomin = zoomout = false; + eventData.current.oldLastScale = lastScale; + } + + var extracted = []; + if(lastScale !== -1) { + while(lastScale >= 0) { + if(target.transform[lastScale][0] === "scale") { + extracted.push(target.transform[lastScale]); + target.transform[lastScale] = ["scale"]; + } + lastScale--; + } + } + + var animation = settings.animation; + if(settings.reasonableAnimation[eventData.reason]) { + animation = $.extend({}, + animation, + settings.reasonableAnimation[eventData.reason]); + } + + props = { + // to keep the perspective look similar for different scales + // we need to 'scale' the perspective, too + perspective: Math.round(target.perspectiveScale * 1000) + "px" + }; + props = $.extend({}, animation, props); + if (!zoomin) { + props.transitionDelay = '0s'; + } + if (!eventData.beforeActive) { + props.transitionDuration = '0s'; + props.transitionDelay = '0s'; + } + $.jmpress("css", eventData.area, props); + engine.transform(eventData.area, extracted, eventData.settings); + + props = $.extend({}, animation); + if (!zoomout) { + props.transitionDelay = '0s'; + } + if (!eventData.beforeActive) { + props.transitionDuration = '0s'; + props.transitionDelay = '0s'; + } + + eventData.current.perspectiveScale = target.perspectiveScale; + + $.jmpress("css", eventData.canvas, props); + engine.transform(eventData.canvas, target.transform, eventData.settings); + }); + +}(jQuery, document, window)); +/* + * circular.js + * Repeat from start after end + */ +(function( $, document, window, undefined ) { + + 'use strict'; + var $jmpress = $.jmpress; + + /* FUNCTIONS */ + function firstSlide( step, eventData ) { + return $(this).find(eventData.settings.stepSelector).first(); + } + function prevOrNext( jmpress, step, eventData, prev) { + if (!step) { + return false; + } + var stepSelector = eventData.settings.stepSelector; + step = $(step); + do { + var item = step.near( stepSelector, prev ); + if (item.length === 0 || item.closest(jmpress).length === 0) { + item = $(jmpress).find(stepSelector)[prev?"last":"first"](); + } + if (!item.length) { + return false; + } + step = item; + } while( step.data("stepData").exclude ); + return step; + } + + /* HOOKS */ + $jmpress( 'initStep', function( step, eventData ) { + eventData.stepData.exclude = eventData.data.exclude && ["false", "no"].indexOf(eventData.data.exclude) === -1; + }); + $jmpress( 'selectInitialStep', firstSlide); + $jmpress( 'selectHome', firstSlide); + $jmpress( 'selectEnd', function( step, eventData ) { + return $(this).find(eventData.settings.stepSelector).last(); + }); + $jmpress( 'selectPrev', function( step, eventData ) { + return prevOrNext(this, step, eventData, true); + }); + $jmpress( 'selectNext', function( step, eventData ) { + return prevOrNext(this, step, eventData); + }); +}(jQuery, document, window)); +/* + * start.js + * Set the first step to start on + */ +(function( $, document, window, undefined ) { + + 'use strict'; + + /* HOOKS */ + $.jmpress( 'selectInitialStep', function( nil, eventData ) { + return eventData.settings.start; + }); + +}(jQuery, document, window)); +/* + * ways.js + * Control the flow of the steps + */ +(function( $, document, window, undefined ) { + + 'use strict'; + var $jmpress = $.jmpress; + + /* FUNCTIONS */ + function routeFunc( jmpress, route, type ) { + for(var i = 0; i < route.length - 1; i++) { + var from = route[i]; + var to = route[i+1]; + if($(jmpress).jmpress("initialized")) { + $(from, jmpress).data("stepData")[type] = to; + } else { + $(from, jmpress).attr('data-' + type, to); + } + } + } + function selectPrevOrNext( step, eventData, attr, prev ) { + var stepData = eventData.stepData; + if(stepData[attr]) { + var near = $(step).near(stepData[attr], prev); + if(near && near.length) { + return near; + } + near = $(stepData[attr], this)[prev?"last":"first"](); + if(near && near.length) { + return near; + } + } + } + + /* EXPORTED FUNCTIONS */ + $jmpress( 'register', 'route', function( route, unidirectional, reversedRoute ) { + if( typeof route === "string" ) { + route = [route, route]; + } + routeFunc(this, route, reversedRoute ? "prev" : "next"); + if (!unidirectional) { + routeFunc(this, route.reverse(), reversedRoute ? "next" : "prev"); + } + }); + + /* HOOKS */ + $jmpress( 'initStep', function( step, eventData ) { + for(var attr in {next:1,prev:1}) { + eventData.stepData[attr] = eventData.data[attr]; + } + }); + $jmpress( 'selectNext', function( step, eventData ) { + return selectPrevOrNext.call(this, step, eventData, "next"); + }); + $jmpress( 'selectPrev', function( step, eventData ) { + return selectPrevOrNext.call(this, step, eventData, "prev", true); + }); + +}(jQuery, document, window)); +/* + * ajax.js + * Load steps via ajax + */ +(function( $, document, window, undefined ) { + + 'use strict'; + var $jmpress = $.jmpress; + + /* DEFINES */ + var afterStepLoaded = 'ajax:afterStepLoaded', + loadStep = 'ajax:loadStep'; + + /* REGISTER EVENTS */ + $jmpress('register', loadStep); + $jmpress('register', afterStepLoaded); + + /* DEFAULTS */ + $jmpress('defaults').ajaxLoadedClass = "loaded"; + + /* HOOKS */ + $jmpress('initStep', function( step, eventData ) { + eventData.stepData.src = $(step).attr('href') || eventData.data.src || false; + eventData.stepData.srcLoaded = false; + }); + $jmpress(loadStep, function( step, eventData ) { + var stepData = eventData.stepData, + href = stepData && stepData.src, + settings = eventData.settings; + if ( href ) { + $(step).addClass( settings.ajaxLoadedClass ); + stepData.srcLoaded = true; + $(step).load(href, function(response, status, xhr) { + $(eventData.jmpress).jmpress('fire', afterStepLoaded, step, $.extend({}, eventData, { + response: response + ,status: status + ,xhr: xhr + })); + }); + } + }); + $jmpress('idle', function( step, eventData ) { + if (!step) { + return; + } + var settings = eventData.settings, + jmpress = $(this), + stepData = eventData.stepData; + var siblings = $(step) + .add( $(step).near( settings.stepSelector ) ) + .add( $(step).near( settings.stepSelector, true) ) + .add( jmpress.jmpress('fire', 'selectPrev', step, { + stepData: $(step).data('stepData') + })) + .add( jmpress.jmpress('fire', 'selectNext', step, { + stepData: $(step).data('stepData') + })); + siblings.each(function() { + var step = this, + stepData = $(step).data("stepData"); + if(!stepData.src || stepData.srcLoaded) { + return; + } + jmpress.jmpress('fire', loadStep, step, { + stepData: $(step).data('stepData') + }); + }); + }); + $jmpress("setActive", function(step, eventData) { + var stepData = $(step).data("stepData"); + if(!stepData.src || stepData.srcLoaded) { + return; + } + $(this).jmpress('fire', loadStep, step, { + stepData: $(step).data('stepData') + }); + }); + +}(jQuery, document, window)); +/* + * hash.js + * Detect and set the URL hash + */ +(function( $, document, window, undefined ) { + + 'use strict'; + var $jmpress = $.jmpress, + hashLink = "a[href^=#]"; + + /* FUNCTIONS */ + function randomString() { + return "" + Math.round(Math.random() * 100000, 0); + } + /** + * getElementFromUrl + * + * @return String or undefined + */ + function getElementFromUrl(settings) { + // get id from url # by removing `#` or `#/` from the beginning, + // so both "fallback" `#slide-id` and "enhanced" `#/slide-id` will work + // TODO SECURITY check user input to be valid! + try { + var el = $( '#' + window.location.hash.replace(/^#\/?/,"") ); + return el.length > 0 && el.is(settings.stepSelector) ? el : undefined; + } catch(e) {} + } + function setHash(stepid) { + var shouldBeHash = "#/" + stepid; + if(window.history && window.history.pushState) { + // shouldBeHash = "#" + stepid; + // consider this for future versions + // it has currently issues, when startup with a link with hash (webkit) + if(window.location.hash !== shouldBeHash) { + window.history.pushState({}, '', shouldBeHash); + } + } else { + if(window.location.hash !== shouldBeHash) { + window.location.hash = shouldBeHash; + } + } + } + + /* DEFAULTS */ + $jmpress('defaults').hash = { + use: true + ,update: true + ,bindChange: true + // NOTICE: {use: true, update: false, bindChange: true} + // will cause a error after clicking on a link to the current step + }; + + /* HOOKS */ + $jmpress('selectInitialStep', function( step, eventData ) { + var settings = eventData.settings, + hashSettings = settings.hash, + current = eventData.current, + jmpress = $(this); + eventData.current.hashNamespace = ".jmpress-"+randomString(); + // HASH CHANGE EVENT + if ( hashSettings.use ) { + if ( hashSettings.bindChange ) { + $(window).bind('hashchange'+current.hashNamespace, function(event) { + var urlItem = getElementFromUrl(settings); + if ( jmpress.jmpress('initialized') ) { + jmpress.jmpress("scrollFix"); + } + if(urlItem && urlItem.length) { + if(urlItem.attr("id") !== jmpress.jmpress("active").attr("id")) { + jmpress.jmpress('select', urlItem); + } + setHash(urlItem.attr("id")); + } + event.preventDefault(); + }); + $(hashLink).on("click"+current.hashNamespace, function(event) { + var href = $(this).attr("href"); + try { + if($(href).is(settings.stepSelector)) { + jmpress.jmpress("select", href); + event.preventDefault(); + event.stopPropagation(); + } + } catch(e) {} + }); + } + return getElementFromUrl(settings); + } + }); + $jmpress('afterDeinit', function( nil, eventData ) { + $(hashLink).off(eventData.current.hashNamespace); + $(window).unbind(eventData.current.hashNamespace); + }); + $jmpress('setActive', function( step, eventData ) { + var settings = eventData.settings, + current = eventData.current; + // `#/step-id` is used instead of `#step-id` to prevent default browser + // scrolling to element in hash + if ( settings.hash.use && settings.hash.update ) { + clearTimeout(current.hashtimeout); + current.hashtimeout = setTimeout(function() { + setHash($(eventData.delegatedFrom).attr('id')); + }, settings.transitionDuration + 200); + } + }); + +}(jQuery, document, window)); +/* + * keyboard.js + * Keyboard event mapping and default keyboard actions + */ +(function( $, document, window, undefined ) { + + 'use strict'; + var $jmpress = $.jmpress, + jmpressNext = "next", + jmpressPrev = "prev"; + + /* FUNCTIONS */ + function randomString() { + return "" + Math.round(Math.random() * 100000, 0); + } + function stopEvent(event) { + event.preventDefault(); + event.stopPropagation(); + } + + /* DEFAULTS */ + $jmpress('defaults').keyboard = { + use: true + ,keys: { + 33: jmpressPrev // pg up + ,37: jmpressPrev // left + ,38: jmpressPrev // up + + ,9: jmpressNext+":"+jmpressPrev // tab + ,32: jmpressNext // space + ,34: jmpressNext // pg down + ,39: jmpressNext // right + ,40: jmpressNext // down + + ,36: "home" // home + + ,35: "end" // end + } + ,ignore: { + "INPUT": [ + 32 // space + ,37 // left + ,38 // up + ,39 // right + ,40 // down + ] + ,"TEXTAREA": [ + 32 // space + ,37 // left + ,38 // up + ,39 // right + ,40 // down + ] + ,"SELECT": [ + 38 // up + ,40 // down + ] + } + ,tabSelector: "a[href]:visible, :input:visible" + }; + + /* HOOKS */ + $jmpress('afterInit', function( nil, eventData ) { + var settings = eventData.settings, + keyboardSettings = settings.keyboard, + ignoreKeyboardSettings = keyboardSettings.ignore, + current = eventData.current, + jmpress = $(this); + + // tabindex make it focusable so that it can recieve key events + if(!settings.fullscreen) { + jmpress.attr("tabindex", 0); + } + + current.keyboardNamespace = ".jmpress-"+randomString(); + + // KEYPRESS EVENT: this fixes a Opera bug + $(settings.fullscreen ? document : jmpress) + .bind("keypress"+current.keyboardNamespace, function( event ) { + + for( var nodeName in ignoreKeyboardSettings ) { + if ( event.target.nodeName === nodeName && ignoreKeyboardSettings[nodeName].indexOf(event.which) !== -1 ) { + return; + } + } + if(event.which >= 37 && event.which <= 40 || event.which === 32) { + stopEvent(event); + } + }); + // KEYDOWN EVENT + $(settings.fullscreen ? document : jmpress) + .bind("keydown"+current.keyboardNamespace, function( event ) { + var eventTarget = $(event.target); + + if ( !settings.fullscreen && !eventTarget.closest(jmpress).length || !keyboardSettings.use ) { + return; + } + + for( var nodeName in ignoreKeyboardSettings ) { + if ( eventTarget[0].nodeName === nodeName && ignoreKeyboardSettings[nodeName].indexOf(event.which) !== -1 ) { + return; + } + } + + var reverseSelect = false; + var nextFocus; + if (event.which === 9) { + // tab + if ( !eventTarget.closest( jmpress.jmpress('active') ).length ) { + if ( !event.shiftKey ) { + nextFocus = jmpress.jmpress('active').find("a[href], :input").filter(":visible").first(); + } else { + reverseSelect = true; + } + } else { + nextFocus = eventTarget.near( keyboardSettings.tabSelector, event.shiftKey ); + if( !$(nextFocus) + .closest( settings.stepSelector ) + .is(jmpress.jmpress('active') ) ) { + nextFocus = undefined; + } + } + if( nextFocus && nextFocus.length > 0 ) { + nextFocus.focus(); + jmpress.jmpress("scrollFix"); + stopEvent(event); + return; + } else { + if(event.shiftKey) { + reverseSelect = true; + } + } + } + + var action = keyboardSettings.keys[ event.which ]; + if ( typeof action === "string" ) { + if (action.indexOf(":") !== -1) { + action = action.split(":"); + action = event.shiftKey ? action[1] : action[0]; + } + jmpress.jmpress( action ); + stopEvent(event); + } else if ( $.isFunction(action) ) { + action.call(jmpress, event); + } else if ( action ) { + jmpress.jmpress.apply( jmpress, action ); + stopEvent(event); + } + + if (reverseSelect) { + // tab + nextFocus = jmpress.jmpress('active').find("a[href], :input").filter(":visible").last(); + nextFocus.focus(); + jmpress.jmpress("scrollFix"); + } + }); + }); + $jmpress('afterDeinit', function( nil, eventData ) { + $(document).unbind(eventData.current.keyboardNamespace); + }); + + +}(jQuery, document, window)); +/* + * viewport.js + * Scale to fit a given viewport + */ +(function( $, document, window, undefined ) { + + 'use strict'; + + function randomString() { + return "" + Math.round(Math.random() * 100000, 0); + } + + var browser = (function() { + var ua = navigator.userAgent.toLowerCase(); + var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || + /(webkit)[ \/]([\w.]+)/.exec(ua) || + /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || + /(msie) ([\w.]+)/.exec(ua) || + ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || + []; + return match[1] || ""; + }()); + + var defaults = $.jmpress("defaults"); + defaults.viewPort = { + width: false + ,height: false + ,maxScale: 0 + ,minScale: 0 + ,zoomable: 0 + ,zoomBindMove: true + ,zoomBindWheel: true + }; + var keys = defaults.keyboard.keys; + keys[browser === 'mozilla' ? 107 : 187] = "zoomIn"; // + + keys[browser === 'mozilla' ? 109 : 189] = "zoomOut"; // - + defaults.reasonableAnimation.resize = { + transitionDuration: '0s' + ,transitionDelay: '0ms' + }; + defaults.reasonableAnimation.zoom = { + transitionDuration: '0s' + ,transitionDelay: '0ms' + }; + $.jmpress("initStep", function( step, eventData ) { + for(var variable in {"viewPortHeight":1, "viewPortWidth":1, "viewPortMinScale":1, "viewPortMaxScale":1, "viewPortZoomable":1}) { + eventData.stepData[variable] = eventData.data[variable] && parseFloat(eventData.data[variable]); + } + }); + $.jmpress("afterInit", function( nil, eventData ) { + var jmpress = this; + eventData.current.viewPortNamespace = ".jmpress-"+randomString(); + $(window).bind("resize"+eventData.current.viewPortNamespace, function (event) { + $(jmpress).jmpress("reselect", "resize"); + }); + eventData.current.userZoom = 0; + eventData.current.userTranslateX = 0; + eventData.current.userTranslateY = 0; + if(eventData.settings.viewPort.zoomBindWheel) { + $(eventData.settings.fullscreen ? document : this) + .bind("mousewheel"+eventData.current.viewPortNamespace+" DOMMouseScroll"+eventData.current.viewPortNamespace, function( event, delta ) { + delta = delta || event.originalEvent.wheelDelta || -event.originalEvent.detail /* mozilla */; + var direction = (delta / Math.abs(delta)); + if(direction < 0) { + $(eventData.jmpress).jmpress("zoomOut", event.originalEvent.clientX, event.originalEvent.clientY); + } else if(direction > 0) { + $(eventData.jmpress).jmpress("zoomIn", event.originalEvent.clientX, event.originalEvent.clientY); + } + return false; + }); + } + if(eventData.settings.viewPort.zoomBindMove) { + $(eventData.settings.fullscreen ? document : this).bind("mousedown"+eventData.current.viewPortNamespace, function (event) { + if(eventData.current.userZoom) { + eventData.current.userTranslating = { x: event.clientX, y: event.clientY }; + event.preventDefault(); + event.stopImmediatePropagation(); + } + }).bind("mousemove"+eventData.current.viewPortNamespace, function (event) { + var userTranslating = eventData.current.userTranslating; + if(userTranslating) { + $(jmpress).jmpress("zoomTranslate", event.clientX - userTranslating.x, event.clientY - userTranslating.y); + userTranslating.x = event.clientX; + userTranslating.y = event.clientY; + event.preventDefault(); + event.stopImmediatePropagation(); + } + }).bind("mouseup"+eventData.current.viewPortNamespace, function (event) { + if(eventData.current.userTranslating) { + eventData.current.userTranslating = undefined; + event.preventDefault(); + event.stopImmediatePropagation(); + } + }); + } + }); + function maxAbs(value, range) { + return Math.max(Math.min(value, range), -range); + } + function zoom(x, y, direction) { + var current = $(this).jmpress("current"), + settings = $(this).jmpress("settings"), + stepData = $(this).jmpress("active").data("stepData"), + container = $(this).jmpress("container"); + if(current.userZoom === 0 && direction < 0) { + return; + } + var zoomableSteps = stepData.viewPortZoomable || settings.viewPort.zoomable; + if(current.userZoom === zoomableSteps && direction > 0) { + return; + } + current.userZoom += direction; + + var halfWidth = $(container).innerWidth()/2, + halfHeight = $(container).innerHeight()/2; + + x = x ? x - halfWidth : x; + y = y ? y - halfHeight : y; + + // TODO this is not perfect... too much math... :( + current.userTranslateX = + maxAbs(current.userTranslateX - direction * x / current.zoomOriginWindowScale / zoomableSteps, + halfWidth * current.userZoom * current.userZoom / zoomableSteps); + current.userTranslateY = + maxAbs(current.userTranslateY - direction * y / current.zoomOriginWindowScale / zoomableSteps, + halfHeight * current.userZoom * current.userZoom / zoomableSteps); + + $(this).jmpress("reselect", "zoom"); + } + $.jmpress("register", "zoomIn", function(x, y) { + zoom.call(this, x||0, y||0, 1); + }); + $.jmpress("register", "zoomOut", function(x, y) { + zoom.call(this, x||0, y||0, -1); + }); + $.jmpress("register", "zoomTranslate", function(x, y) { + var current = $(this).jmpress("current"), + settings = $(this).jmpress("settings"), + stepData = $(this).jmpress("active").data("stepData"), + container = $(this).jmpress("container"); + var zoomableSteps = stepData.viewPortZoomable || settings.viewPort.zoomable; + var halfWidth = $(container).innerWidth(), + halfHeight = $(container).innerHeight(); + current.userTranslateX = + maxAbs(current.userTranslateX + x / current.zoomOriginWindowScale, + halfWidth * current.userZoom * current.userZoom / zoomableSteps); + current.userTranslateY = + maxAbs(current.userTranslateY + y / current.zoomOriginWindowScale, + halfHeight * current.userZoom * current.userZoom / zoomableSteps); + $(this).jmpress("reselect", "zoom"); + }); + $.jmpress('afterDeinit', function( nil, eventData ) { + $(eventData.settings.fullscreen ? document : this).unbind(eventData.current.viewPortNamespace); + $(window).unbind(eventData.current.viewPortNamespace); + }); + $.jmpress("setActive", function( step, eventData ) { + var viewPort = eventData.settings.viewPort; + var viewPortHeight = eventData.stepData.viewPortHeight || viewPort.height; + var viewPortWidth = eventData.stepData.viewPortWidth || viewPort.width; + var viewPortMaxScale = eventData.stepData.viewPortMaxScale || viewPort.maxScale; + var viewPortMinScale = eventData.stepData.viewPortMinScale || viewPort.minScale; + // Correct the scale based on the window's size + var windowScaleY = viewPortHeight && $(eventData.container).innerHeight()/viewPortHeight; + var windowScaleX = viewPortWidth && $(eventData.container).innerWidth()/viewPortWidth; + var windowScale = (windowScaleX || windowScaleY) && Math.min( windowScaleX || windowScaleY, windowScaleY || windowScaleX ); + + if(windowScale) { + windowScale = windowScale || 1; + if(viewPortMaxScale) { + windowScale = Math.min(windowScale, viewPortMaxScale); + } + if(viewPortMinScale) { + windowScale = Math.max(windowScale, viewPortMinScale); + } + + var zoomableSteps = eventData.stepData.viewPortZoomable || eventData.settings.viewPort.zoomable; + if(zoomableSteps) { + var diff = (1/windowScale) - (1/viewPortMaxScale); + diff /= zoomableSteps; + windowScale = 1/((1/windowScale) - diff * eventData.current.userZoom); + } + + eventData.target.transform.reverse(); + if(eventData.current.userTranslateX && eventData.current.userTranslateY) { + eventData.target.transform.push(["translate", eventData.current.userTranslateX, eventData.current.userTranslateY, 0]); + } else { + eventData.target.transform.push(["translate"]); + } + eventData.target.transform.push(["scale", + windowScale, + windowScale, + 1]); + eventData.target.transform.reverse(); + eventData.target.perspectiveScale /= windowScale; + } + eventData.current.zoomOriginWindowScale = windowScale; + }); + $.jmpress("setInactive", function( step, eventData ) { + if(!eventData.nextStep || !step || $(eventData.nextStep).attr("id") !== $(step).attr("id")) { + eventData.current.userZoom = 0; + eventData.current.userTranslateX = 0; + eventData.current.userTranslateY = 0; + } + }); + +}(jQuery, document, window)); + +/* + * mouse.js + * Clicking to select a step + */ +(function( $, document, window, undefined ) { + + 'use strict'; + var $jmpress = $.jmpress; + + /* FUNCTIONS */ + function randomString() { + return "" + Math.round(Math.random() * 100000, 0); + } + + /* DEFAULTS */ + $jmpress("defaults").mouse = { + clickSelects: true + }; + + /* HOOKS */ + $jmpress("afterInit", function( nil, eventData ) { + var settings = eventData.settings, + stepSelector = settings.stepSelector, + current = eventData.current, + jmpress = $(this); + current.clickableStepsNamespace = ".jmpress-"+randomString(); + jmpress.bind("click"+current.clickableStepsNamespace, function(event) { + if (!settings.mouse.clickSelects || current.userZoom) { + return; + } + + // get clicked step + var clickedStep = $(event.target).closest(stepSelector); + + // clicks on the active step do default + if ( clickedStep.is( jmpress.jmpress("active") ) ) { + return; + } + + if (clickedStep.length) { + // select the clicked step + jmpress.jmpress("select", clickedStep[0], "click"); + event.preventDefault(); + event.stopPropagation(); + } + }); + }); + $jmpress('afterDeinit', function( nil, eventData ) { + $(this).unbind(eventData.current.clickableStepsNamespace); + }); + +}(jQuery, document, window)); +/* + * mobile.js + * Adds support for swipe on touch supported browsers + */ +(function( $, document, window, undefined ) { + + 'use strict'; + var $jmpress = $.jmpress; + + /* FUNCTIONS */ + function randomString() { + return "" + Math.round(Math.random() * 100000, 0); + } + + /* HOOKS */ + $jmpress( 'afterInit', function( step, eventData ) { + var settings = eventData.settings, + current = eventData.current, + jmpress = eventData.jmpress; + current.mobileNamespace = ".jmpress-"+randomString(); + var data, start = [0,0]; + $(settings.fullscreen ? document : jmpress) + .bind("touchstart"+current.mobileNamespace, function( event ) { + + data = event.originalEvent.touches[0]; + start = [ data.pageX, data.pageY ]; + + }).bind("touchmove"+current.mobileNamespace, function( event ) { + data = event.originalEvent.touches[0]; + event.preventDefault(); + return false; + }).bind("touchend"+current.mobileNamespace, function( event ) { + var end = [ data.pageX, data.pageY ], + diff = [ end[0]-start[0], end[1]-start[1] ]; + + if(Math.max(Math.abs(diff[0]), Math.abs(diff[1])) > 50) { + diff = Math.abs(diff[0]) > Math.abs(diff[1]) ? diff[0] : diff[1]; + $(jmpress).jmpress(diff > 0 ? "prev" : "next"); + event.preventDefault(); + return false; + } + }); + }); + $jmpress('afterDeinit', function( nil, eventData ) { + var settings = eventData.settings, + current = eventData.current, + jmpress = eventData.jmpress; + $(settings.fullscreen ? document : jmpress).unbind(current.mobileNamespace); + }); + +}(jQuery, document, window)); +/* + * templates.js + * The amazing template engine + */ +(function( $, document, window, undefined ) { + + 'use strict'; + var $jmpress = $.jmpress, + templateFromParentIdent = "_template_", + templateFromApplyIdent = "_applied_template_"; + + /* STATIC VARS */ + var templates = {}; + + /* FUNCTIONS */ + function addUndefined( target, values, prefix ) { + for( var name in values ) { + var targetName = name; + if ( prefix ) { + targetName = prefix + targetName.substr(0, 1).toUpperCase() + targetName.substr(1); + } + if ( $.isPlainObject(values[name]) ) { + addUndefined( target, values[name], targetName ); + } else if( target[targetName] === undefined ) { + target[targetName] = values[name]; + } + } + } + function applyChildrenTemplates( children, templateChildren ) { + if ($.isArray(templateChildren)) { + if (templateChildren.length < children.length) { + $.error("more nested steps than children in template"); + } else { + children.each(function(idx, child) { + child = $(child); + var tmpl = child.data(templateFromParentIdent) || {}; + addUndefined(tmpl, templateChildren[idx]); + child.data(templateFromParentIdent, tmpl); + }); + } + } else if($.isFunction(templateChildren)) { + children.each(function(idx, child) { + child = $(child); + var tmpl = child.data(templateFromParentIdent) || {}; + addUndefined(tmpl, templateChildren(idx, child, children)); + child.data(templateFromParentIdent, tmpl); + }); + } // TODO: else if(object) + } + function applyTemplate( data, element, template, eventData ) { + if (template.children) { + var children = element.children( eventData.settings.stepSelector ); + applyChildrenTemplates( children, template.children ); + } + applyTemplateData( data, template ); + } + function applyTemplateData( data, template ) { + addUndefined(data, template); + } + + /* HOOKS */ + $jmpress("beforeInitStep", function( step, eventData ) { + step = $(step); + var data = eventData.data, + templateFromAttr = data.template, + templateFromApply = step.data(templateFromApplyIdent), + templateFromParent = step.data(templateFromParentIdent); + if(templateFromAttr) { + $.each(templateFromAttr.split(" "), function(idx, tmpl) { + var template = templates[tmpl]; + applyTemplate( data, step, template, eventData ); + }); + } + if (templateFromApply) { + applyTemplate( data, step, templateFromApply, eventData ); + } + if (templateFromParent) { + applyTemplate( data, step, templateFromParent, eventData ); + step.data(templateFromParentIdent, null); + if(templateFromParent.template) { + $.each(templateFromParent.template.split(" "), function(idx, tmpl) { + var template = templates[tmpl]; + applyTemplate( data, step, template, eventData ); + }); + } + } + }); + $jmpress("beforeInit", function( nil, eventData ) { + var data = $jmpress("dataset", this), + dataTemplate = data.template, + stepSelector = eventData.settings.stepSelector; + if (dataTemplate) { + var template = templates[dataTemplate]; + applyChildrenTemplates( $(this).find(stepSelector).filter(function() { + return !$(this).parent().is(stepSelector); + }), template.children ); + } + }); + + /* EXPORTED FUNCTIONS */ + $jmpress("register", "template", function( name, tmpl ) { + if (templates[name]) { + templates[name] = $.extend(true, {}, templates[name], tmpl); + } else { + templates[name] = $.extend(true, {}, tmpl); + } + }); + $jmpress("register", "apply", function( selector, tmpl ) { + if( !tmpl ) { + // TODO ERROR because settings not found + var stepSelector = $(this).jmpress("settings").stepSelector; + applyChildrenTemplates( $(this).find(stepSelector).filter(function() { + return !$(this).parent().is(stepSelector); + }), selector ); + } else if($.isArray(tmpl)) { + applyChildrenTemplates( $(selector), tmpl ); + } else { + var template; + if(typeof tmpl === "string") { + template = templates[tmpl]; + } else { + template = $.extend(true, {}, tmpl); + } + $(selector).each(function(idx, element) { + element = $(element); + var tmpl = element.data(templateFromApplyIdent) || {}; + addUndefined(tmpl, template); + element.data(templateFromApplyIdent, tmpl); + }); + } + }); + +}(jQuery, document, window)); +/* + * jqevents.js + * Fires jQuery events + */ +(function( $, document, window, undefined ) { + + 'use strict'; + + /* HOOKS */ + // the events should not bubble up the tree + // elsewise nested jmpress would cause buggy behavior + $.jmpress("setActive", function( step, eventData ) { + if(eventData.prevStep !== step) { + $(step).triggerHandler("enterStep"); + } + }); + $.jmpress("setInactive", function( step, eventData ) { + if(eventData.nextStep !== step) { + $(step).triggerHandler("leaveStep"); + } + }); + +}(jQuery, document, window)); +/* + * animation.js + * Apply custom animations to steps + */ +(function( $, document, window, undefined ) { + + 'use strict'; + + function parseSubstepInfo(str) { + var arr = str.split(" "); + var className = arr[0]; + var config = { willClass: "will-"+className, doClass: "do-"+className, hasClass: "has-"+className }; + var state = ""; + for(var i = 1; i < arr.length; i++) { + var s = arr[i]; + switch(state) { + case "": + if(s === "after") { + state = "after"; + } else { + $.warn("unknown keyword in '"+str+"'. '"+s+"' unknown."); + } + break; + case "after": + if(s.match(/^[1-9][0-9]*m?s?/)) { + var value = parseFloat(s); + if(s.indexOf("ms") !== -1) { + value *= 1; + } else if(s.indexOf("s") !== -1) { + value *= 1000; + } else if(s.indexOf("m") !== -1) { + value *= 60000; + } + config.delay = value; + } else { + config.after = Array.prototype.slice.call(arr, i).join(" "); + i = arr.length; + } + } + } + return config; + } + function find(array, selector, start, end) { + end = end || (array.length - 1); + start = start || 0; + for(var i = start; i < end + 1; i++) { + if($(array[i].element).is(selector)) { + return i; + } + } + } + function addOn(list, substep, delay) { + $.each(substep._on, function(idx, child) { + list.push({substep: child.substep, delay: child.delay + delay}); + addOn(list, child.substep, child.delay + delay); + }); + } + $.jmpress("defaults").customAnimationDataAttribute = "jmpress"; + $.jmpress("afterInit", function( nil, eventData ) { + eventData.current.animationTimeouts = []; + eventData.current.animationCleanupWaiting = []; + }); + $.jmpress("applyStep", function( step, eventData ) { + // read custom animation from elements + var substepsData = {}; + var listOfSubsteps = []; + $(step).find("[data-"+eventData.settings.customAnimationDataAttribute+"]") + .each(function(idx, element) { + if($(element).closest(eventData.settings.stepSelector).is(step)) { + listOfSubsteps.push({element: element}); + } + }); + if(listOfSubsteps.length === 0) { + return; + } + $.each(listOfSubsteps, function(idx, substep) { + substep.info = parseSubstepInfo( + $(substep.element).data(eventData.settings.customAnimationDataAttribute)); + $(substep.element).addClass(substep.info.willClass); + substep._on = []; + substep._after = null; + }); + var current = {_after: undefined, _on: [], info: {}}; // virtual zero step + $.each(listOfSubsteps, function(idx, substep) { + var other = substep.info.after; + if(other) { + if(other === "step") { + other = current; + } else if(other === "prev") { + other = listOfSubsteps[idx-1]; + } else { + var index = find(listOfSubsteps, other, 0, idx - 1); + if(index === undefined) { + index = find(listOfSubsteps, other); + } + other = (index === undefined || index === idx) ? listOfSubsteps[idx-1] : listOfSubsteps[index]; + } + } else { + other = listOfSubsteps[idx-1]; + } + if(other) { + if(!substep.info.delay) { + if(!other._after) { + other._after = substep; + return; + } + other = other._after; + } + other._on.push({substep: substep, delay: substep.info.delay || 0}); + } + }); + if(current._after === undefined && current._on.length === 0) { + var startStep = find(listOfSubsteps, eventData.stepData.startSubstep) || 0; + current._after = listOfSubsteps[startStep]; + } + var substepsInOrder = []; + function findNextFunc(idx, item) { + if(item.substep._after) { + current = item.substep._after; + return false; + } + } + do { + var substepList = [{substep: current, delay: 0}]; + addOn(substepList, current, 0); + substepsInOrder.push(substepList); + current = null; + $.each(substepList, findNextFunc); + } while(current); + substepsData.list = substepsInOrder; + $(step).data("substepsData", substepsData); + }); + $.jmpress("unapplyStep", function( step, eventData ) { + var substepsData = $(step).data("substepsData"); + if(substepsData) { + $.each(substepsData.list, function(idx, activeSubsteps) { + $.each(activeSubsteps, function(idx, substep) { + if(substep.substep.info.willClass) { + $(substep.substep.element).removeClass(substep.substep.info.willClass); + } + if(substep.substep.info.hasClass) { + $(substep.substep.element).removeClass(substep.substep.info.hasClass); + } + if(substep.substep.info.doClass) { + $(substep.substep.element).removeClass(substep.substep.info.doClass); + } + }); + }); + } + }); + $.jmpress("setActive", function(step, eventData) { + var substepsData = $(step).data("substepsData"); + if(!substepsData) { + return; + } + if(eventData.substep === undefined) { + eventData.substep = + (eventData.reason === "prev" ? + substepsData.list.length-1 : + 0 + ); + } + var substep = eventData.substep; + $.each(eventData.current.animationTimeouts, function(idx, timeout) { + clearTimeout(timeout); + }); + eventData.current.animationTimeouts = []; + $.each(substepsData.list, function(idx, activeSubsteps) { + var applyHas = idx < substep; + var applyDo = idx <= substep; + $.each(activeSubsteps, function(idx, substep) { + if(substep.substep.info.hasClass) { + $(substep.substep.element)[(applyHas?"add":"remove")+"Class"](substep.substep.info.hasClass); + } + function applyIt() { + $(substep.substep.element).addClass(substep.substep.info.doClass); + } + if(applyDo && !applyHas && substep.delay && eventData.reason !== "prev") { + if(substep.substep.info.doClass) { + $(substep.substep.element).removeClass(substep.substep.info.doClass); + eventData.current.animationTimeouts.push(setTimeout(applyIt, substep.delay)); + } + } else { + if(substep.substep.info.doClass) { + $(substep.substep.element)[(applyDo?"add":"remove")+"Class"](substep.substep.info.doClass); + } + } + }); + }); + }); + $.jmpress("setInactive", function(step, eventData) { + if(eventData.nextStep === step) { + return; + } + function cleanupAnimation( substepsData ) { + $.each(substepsData.list, function(idx, activeSubsteps) { + $.each(activeSubsteps, function(idx, substep) { + if(substep.substep.info.hasClass) { + $(substep.substep.element).removeClass(substep.substep.info.hasClass); + } + if(substep.substep.info.doClass) { + $(substep.substep.element).removeClass(substep.substep.info.doClass); + } + }); + }); + } + $.each(eventData.current.animationCleanupWaiting, function(idx, item) { + cleanupAnimation(item); + }); + eventData.current.animationCleanupWaiting = []; + var substepsData = $(step).data("substepsData"); + if(substepsData) { + eventData.current.animationCleanupWaiting.push( substepsData ); + } + }); + $.jmpress("selectNext", function( step, eventData ) { + if(eventData.substep === undefined) { + return; + } + var substepsData = $(step).data("substepsData"); + if(!substepsData) { + return; + } + if(eventData.substep < substepsData.list.length-1) { + return {step: step, substep: eventData.substep+1}; + } + }); + $.jmpress("selectPrev", function( step, eventData ) { + if(eventData.substep === undefined) { + return; + } + var substepsData = $(step).data("substepsData"); + if(!substepsData) { + return; + } + if(eventData.substep > 0) { + return {step: step, substep: eventData.substep-1}; + } + }); + +}(jQuery, document, window)); +/* + * jmpress.toggle plugin + * For binding a key to toggle de/initialization of jmpress.js. + */ +(function( $, document, window, undefined ) { + 'use strict'; + $.jmpress("register", "toggle", function( key, config, initial ) { + var jmpress = this; + $(document).bind("keydown", function( event ) { + if ( event.keyCode === key ) { + if ($(jmpress).jmpress("initialized")) { + $(jmpress).jmpress("deinit"); + } else { + $(jmpress).jmpress(config); + } + } + }); + if ( initial ) { + $(jmpress).jmpress(config); + } + }); +}(jQuery, document, window)); + +/* + * jmpress.secondary plugin + * Apply a secondary animation when step is selected. + */ +(function( $, document, window, undefined ) { + 'use strict'; + $.jmpress("initStep", function( step, eventData ) { + for(var name in eventData.data) { + if(name.indexOf("secondary") === 0) { + eventData.stepData[name] = eventData.data[name]; + } + } + }); + function exchangeIf(childStepData, condition, step) { + if(childStepData.secondary && + childStepData.secondary.split(" ").indexOf(condition) !== -1) { + for(var name in childStepData) { + if(name.length > 9 && name.indexOf("secondary") === 0) { + var tmp = childStepData[name]; + var normal = name.substr(9); + normal = normal.substr(0, 1).toLowerCase() + normal.substr(1); + childStepData[name] = childStepData[normal]; + childStepData[normal] = tmp; + } + } + $(this).jmpress("reapply", $(step)); + } + } + $.jmpress("beforeActive", function( step, eventData ) { + exchangeIf.call(eventData.jmpress, $(step).data("stepData"), "self", step); + var parent = $(step).parent(); + $(parent) + .children(eventData.settings.stepSelector) + .each(function(idx, child) { + var childStepData = $(child).data("stepData"); + exchangeIf.call(eventData.jmpress, childStepData, "siblings", child); + }); + function grandchildrenFunc(idx, child) { + var childStepData = $(child).data("stepData"); + exchangeIf.call(eventData.jmpress, childStepData, "grandchildren", child); + } + for(var i = 1; i < eventData.parents.length; i++) { + $(eventData.parents[i]) + .children(eventData.settings.stepSelector) + .each(); + } + }); + $.jmpress("setInactive", function( step, eventData ) { + exchangeIf.call(eventData.jmpress, $(step).data("stepData"), "self", step); + var parent = $(step).parent(); + $(parent) + .children(eventData.settings.stepSelector) + .each(function(idx, child) { + var childStepData = $(child).data("stepData"); + exchangeIf.call(eventData.jmpress, childStepData, "siblings", child); + }); + function grandchildrenFunc(idx, child) { + var childStepData = $(child).data("stepData"); + exchangeIf.call(eventData.jmpress, childStepData, "grandchildren", child); + } + for(var i = 1; i < eventData.parents.length; i++) { + $(eventData.parents[i]) + .children(eventData.settings.stepSelector) + .each(grandchildrenFunc); + } + }); +}(jQuery, document, window)); + +/* + * jmpress.duration plugin + * For auto advancing steps after a given duration and optionally displaying a + * progress bar. + */ +(function( $, document, window, undefined ) { + 'use strict'; + + $.jmpress("defaults").duration = { + defaultValue: -1 + ,defaultAction: "next" + ,barSelector: undefined + ,barProperty: "width" + ,barPropertyStart: "0" + ,barPropertyEnd: "100%" + }; + $.jmpress("initStep", function( step, eventData ) { + eventData.stepData.duration = eventData.data.duration && parseInt(eventData.data.duration, 10); + eventData.stepData.durationAction = eventData.data.durationAction; + }); + $.jmpress("setInactive", function( step, eventData ) { + var settings = eventData.settings, + durationSettings = settings.duration, + current = eventData.current; + var dur = eventData.stepData.duration || durationSettings.defaultValue; + if( current.durationTimeout ) { + if( durationSettings.barSelector ) { + var css = { + transitionProperty: durationSettings.barProperty + ,transitionDuration: '0' + ,transitionDelay: '0' + ,transitionTimingFunction: 'linear' + }; + css[durationSettings.barProperty] = durationSettings.barPropertyStart; + var bars = $(durationSettings.barSelector); + $.jmpress("css", bars, css); + bars.each(function(idx, element) { + var next = $(element).next(); + var parent = $(element).parent(); + $(element).detach(); + if(next.length) { + next.insertBefore(element); + } else { + parent.append(element); + } + }); + } + clearTimeout(current.durationTimeout); + delete current.durationTimeout; + } + }); + $.jmpress("setActive", function( step, eventData ) { + var settings = eventData.settings, + durationSettings = settings.duration, + current = eventData.current; + var dur = eventData.stepData.duration || durationSettings.defaultValue; + if( dur && dur > 0 ) { + if( durationSettings.barSelector ) { + var css = { + transitionProperty: durationSettings.barProperty + ,transitionDuration: (dur-settings.transitionDuration*2/3-100)+"ms" + ,transitionDelay: (settings.transitionDuration*2/3)+'ms' + ,transitionTimingFunction: 'linear' + }; + css[durationSettings.barProperty] = durationSettings.barPropertyEnd; + $.jmpress("css", $(durationSettings.barSelector), css); + } + var jmpress = this; + if(current.durationTimeout) { + clearTimeout(current.durationTimeout); + current.durationTimeout = undefined; + } + current.durationTimeout = setTimeout(function() { + var action = eventData.stepData.durationAction || durationSettings.defaultAction; + $(jmpress).jmpress(action); + }, dur); + } + }); +}(jQuery, document, window)); + +/* + * jmpress.presentation-mode plugin + * Display a window for the presenter with notes and a control and view of the + * presentation + */ +(function( $, document, window, undefined ) { + + 'use strict'; + var $jmpress = $.jmpress; + + var PREFIX = "jmpress-presentation-"; + + /* FUNCTIONS */ + function randomString() { + return "" + Math.round(Math.random() * 100000, 0); + } + + /* DEFAULTS */ + $jmpress("defaults").presentationMode = { + use: true, + url: "presentation-screen.html", + notesUrl: false, + transferredValues: ["userZoom", "userTranslateX", "userTranslateY"] + }; + $jmpress("defaults").keyboard.keys[80] = "presentationPopup"; // p key + + /* HOOKS */ + $jmpress("afterInit", function( nil, eventData) { + var current = eventData.current; + + current.selectMessageListeners = []; + + if(eventData.settings.presentationMode.use) { + + window.addEventListener("message", function(event) { + // We do not test orgin, because we want to accept messages + // from all orgins + try { + if(typeof event.data !== "string" || event.data.indexOf(PREFIX) !== 0) { + return; + } + var json = JSON.parse(event.data.slice(PREFIX.length)); + switch(json.type) { + case "select": + $.each(eventData.settings.presentationMode.transferredValues, function(idx, name) { + eventData.current[name] = json[name]; + }); + if(/[a-z0-9\-]+/i.test(json.targetId) && typeof json.substep in {number:1,undefined:1}) { + $(eventData.jmpress).jmpress("select", {step: "#"+json.targetId, substep: json.substep}, json.reason); + } else { + $.error("For security reasons the targetId must match /[a-z0-9\\-]+/i and substep must be a number."); + } + break; + case "listen": + current.selectMessageListeners.push(event.source); + break; + case "ok": + clearTimeout(current.presentationPopupTimeout); + break; + case "read": + try { + event.source.postMessage(PREFIX + JSON.stringify({type: "url", url: window.location.href, notesUrl: eventData.settings.presentationMode.notesUrl}), "*"); + } catch(e) { + $.error("Cannot post message to source: " + e); + } + break; + default: + throw "Unknown message type: " + json.type; + } + } catch(e) { + $.error("Received message is malformed: " + e); + } + }); + try { + if(window.parent && window.parent !== window) { + window.parent.postMessage(PREFIX + JSON.stringify({ + "type": "afterInit" + }), "*"); + } + } catch(e) { + $.error("Cannot post message to parent: " + e); + } + } + }); + $jmpress("afterDeinit", function( nil, eventData) { + if(eventData.settings.presentationMode.use) { + try { + if(window.parent && window.parent !== window) { + window.parent.postMessage(PREFIX + JSON.stringify({ + "type": "afterDeinit" + }), "*"); + } + } catch(e) { + $.error("Cannot post message to parent: " + e); + } + } + }); + $jmpress("setActive", function( step, eventData) { + var stepId = $(eventData.delegatedFrom).attr("id"), + substep = eventData.substep, + reason = eventData.reason; + $.each(eventData.current.selectMessageListeners, function(idx, listener) { + try { + var msg = { + "type": "select", + "targetId": stepId, + "substep": substep, + "reason": reason + }; + $.each(eventData.settings.presentationMode.transferredValues, function(idx, name) { + msg[name] = eventData.current[name]; + }); + listener.postMessage(PREFIX + JSON.stringify(msg), "*"); + } catch(e) { + $.error("Cannot post message to listener: " + e); + } + }); + }); + $jmpress("register", "presentationPopup", function() { + function trySend() { + jmpress.jmpress("current").presentationPopupTimeout = setTimeout(trySend, 100); + try { + popup.postMessage(PREFIX + JSON.stringify({type: "url", url: window.location.href, notesUrl: jmpress.jmpress("settings").presentationMode.notesUrl}), "*"); + } catch(e) { + } + } + var jmpress = $(this), + popup; + if(jmpress.jmpress("settings").presentationMode.use) { + popup = window.open($(this).jmpress("settings").presentationMode.url); + jmpress.jmpress("current").presentationPopupTimeout = setTimeout(trySend, 100); + } + }); +}(jQuery, document, window)); diff --git a/app/assets/stylesheets/application.css.scss b/app/assets/stylesheets/application.css.scss index 00161b9..5ff02ef 100755 --- a/app/assets/stylesheets/application.css.scss +++ b/app/assets/stylesheets/application.css.scss @@ -12,6 +12,10 @@ * require_tree . *= require 'calendars' *= require jquery.fileupload-ui +*= require jquery.ui.progressbar +*= require jquery.datetimepicker +*= require jquery.ui.dialog +*= require jquery.ui.tabs * require 'bootstrap' *= require 'tinymce' @@ -147,3 +151,4 @@ background:44F; @import 'galleries'; @import 'tinymce'; +@import 'linkboxes'; \ No newline at end of file diff --git a/app/assets/stylesheets/layout.css.scss b/app/assets/stylesheets/layout.css.scss index 0eb9e1b..2fb10ff 100755 --- a/app/assets/stylesheets/layout.css.scss +++ b/app/assets/stylesheets/layout.css.scss @@ -44,98 +44,6 @@ a:hover { -ul.linkbox-list -{ - max-width:70em; -margin-left:auto; -margin-right:auto; -} -a.linkbox, div.contentbox -{ - display:block; - padding:5px; - border-radius:$box-border-radius; - border: $box-border; - background: $box-background; -} - - -a.linkbox:hover -{ -box-shadow: 1px 1px 2px 2px lightgray; -} - -a.linkbox.color-1 -{ -color:$color_schema_1; -} -a.linkbox.color-2 -{ -color:$color_schema_2; -} -a.linkbox.color-3 -{ -color:$color_schema_3; -} -a.linkbox.color-4 -{ -color:$color_schema_4; -} -a.linkbox.color-5 -{ -color:$color_schema_5; -} - -a.color-1-dark -{ -color:$color_schema_1_dark; -} -a.color-2-dark -{ -color:$color_schema_2_dark; -} -a.color-3-dark -{ -color:$color_schema_3_dark; -} -a.color-4-dark -{ -color:$color_schema_4_dark; -} -a.color-5-dark -{ -color:$color_schema_5_dark; -} -a.color-1-dark:hover -{ -color:$color_schema_1; -} -a.color-2-dark:hover -{ -color:$color_schema_2; -} -a.color-3-dark:hover -{ -color:$color_schema_3; -} -a.color-4-dark:hover -{ -color:$color_schema_4; -} -a.color-5-dark:hover -{ -color:$color_schema_5; -} - - -a.linkbox li -{ -} -ul.linkbox-list li -{ -margin-bottom:10px; -} - div.contentbox { margin: 5px; @@ -175,28 +83,6 @@ div.smallspan { min-height:0; } -ul.linklist -{max-width:100%; -padding:0; -margin:5px; -} -ul.linklist li{ -list-style:none; -margin-bottom:2px; -} -ul.linklist li a i{ -vertical-align:middle; -margin-right:6px; -font-size:2em;} -span.linklist a { - padding:5px; - border-radius:$box-border-radius; - border: $box-border; - background: $box-background; - display:block; - float:left; - -} form.inline div { float:left;} diff --git a/app/assets/stylesheets/themes/blue1/application.css.scss b/app/assets/stylesheets/themes/blue1/application.css.scss index fdc57b0..c150411 100755 --- a/app/assets/stylesheets/themes/blue1/application.css.scss +++ b/app/assets/stylesheets/themes/blue1/application.css.scss @@ -16,6 +16,7 @@ *= require jquery.datetimepicker *= require jquery.ui.dialog *= require jquery.ui.autocomplete + *= require jquery.ui.progressbar *= require jquery.ui.tabs * require 'bootstrap' * require 'neuigkeiten' @@ -117,5 +118,6 @@ $box-border-radius:0px; $box-background: white; @import 'layout'; +@import 'linkboxes'; @import 'calendars'; @import 'formtastic-bootstrap' \ No newline at end of file diff --git a/app/assets/stylesheets/themes/blue1/linkboxes.css.scss b/app/assets/stylesheets/themes/blue1/linkboxes.css.scss new file mode 100644 index 0000000..15bb687 --- /dev/null +++ b/app/assets/stylesheets/themes/blue1/linkboxes.css.scss @@ -0,0 +1,61 @@ +ul.linkbox-list +{ + max-width:70em; + margin-left:auto; + margin-right:auto; +} +a.linkbox, div.contentbox +{ + display:block; + padding:5px; + border-radius:$box-border-radius; + border: $box-border; + background: $box-background; +} + +a.linkbox:hover{ box-shadow: 1px 1px 2px 2px lightgray; } + +a.linkbox.color-1 { color:$color_schema_1; } +a.linkbox.color-2 { color:$color_schema_2; } +a.linkbox.color-3 { color:$color_schema_3; } +a.linkbox.color-4 { color:$color_schema_4;} +a.linkbox.color-5 { color:$color_schema_5;} + +a.color-1-dark { color:$color_schema_1_dark; } +a.color-2-dark { color:$color_schema_2_dark; } +a.color-3-dark { color:$color_schema_3_dark;} +a.color-4-dark { color:$color_schema_4_dark;} +a.color-5-dark { color:$color_schema_5_dark;} +a.color-1-dark:hover { color:$color_schema_1; } +a.color-2-dark:hover { color:$color_schema_2; } +a.color-3-dark:hover { color:$color_schema_3; } +a.color-4-dark:hover { color:$color_schema_4; } +a.color-5-dark:hover { color:$color_schema_5; } + +a.linkbox li { } +ul.linkbox-list li { + margin-bottom:10px; +} +ul.linklist { + max-width:100%; + padding:0; + margin:5px; +} +ul.linklist li { + list-style:none; + margin-bottom:2px; +} +ul.linklist li a i{ + vertical-align:middle; + margin-right:6px; + font-size:2em;} +span.linklist a { + padding:5px; + border-radius:$box-border-radius; + border: $box-border; + background: $box-background; + display:block; + float:left; + +} + diff --git a/app/assets/stylesheets/themes/blue2/application.css.scss b/app/assets/stylesheets/themes/blue2/application.css.scss index 1db0ed0..07388ee 100755 --- a/app/assets/stylesheets/themes/blue2/application.css.scss +++ b/app/assets/stylesheets/themes/blue2/application.css.scss @@ -1,4 +1,6 @@ +/* This is the main file for theme "blue2" + */ /* * This is a manifest file that'll be compiled into application.css, which will include all the files * listed below. @@ -15,6 +17,8 @@ *= require jquery.datetimepicker *= require jquery.ui.dialog *= require jquery.ui.tabs + *= require jquery.ui.progressbar + * require 'bootstrap' * require 'neuigkeiten' @@ -89,9 +93,11 @@ div.header { height:45px; margin-bottom: 0 px } + div.header_span { background:white } + div.header h1 { padding:0; margin:4px; @@ -117,16 +123,16 @@ $navbar-li-minwidth: 0; $box-border: lightgray solid 1px; -$box-border-radius:4px; +$box-border-radius:6px; $box-background: white; @import 'layout'; @import 'calendars'; @import 'formtastic-bootstrap'; - +@import 'linkboxes'; @import 'extra'; body { -background: #FFF; //#EFEFEF; +background: #FCFCFC; //#EFEFEF; background-size:100%; } diff --git a/app/assets/stylesheets/themes/blue2/linkboxes.css.scss b/app/assets/stylesheets/themes/blue2/linkboxes.css.scss new file mode 100644 index 0000000..ea57d92 --- /dev/null +++ b/app/assets/stylesheets/themes/blue2/linkboxes.css.scss @@ -0,0 +1,142 @@ +ul.linkbox-list +{ + max-width:70em; + margin-left:auto; + margin-right:auto; +} + +ul.linkbox-list > li { + margin-bottom:0px; +} + +ul.linkbox-list > li:first-child a.linkbox { + border-top-left-radius:$box-border-radius; + border-top-right-radius:$box-border-radius; + border: $box-border; + +} + +ul.linkbox-list > li:last-child a.linkbox { + border-bottom-left-radius:$box-border-radius; + border-bottom-right-radius:$box-border-radius; +} + +ul.linkbox-list > li a.linkbox { + border-radius: 0; + border-top: none; + +} + +ul.linkbox-list > li a.linkbox:hover { + z-index:2000 +} +a.linkbox, div.contentbox +{ + display:block; + padding:5px; + border-radius:$box-border-radius; + border: $box-border; + background: $box-background; +} + +a.linkbox:hover{ box-shadow: 1px 1px 2px 2px lightgray; } + +a.linkbox.color-1 { color:$color_schema_1; } +a.linkbox.color-2 { color:$color_schema_2; } +a.linkbox.color-3 { color:$color_schema_3; } +a.linkbox.color-4 { color:$color_schema_4;} +a.linkbox.color-5 { color:$color_schema_5;} + +a.color-1-dark { color:$color_schema_1_dark; } +a.color-2-dark { color:$color_schema_2_dark; } +a.color-3-dark { color:$color_schema_3_dark;} +a.color-4-dark { color:$color_schema_4_dark;} +a.color-5-dark { color:$color_schema_5_dark;} +a.color-1-dark:hover { color:$color_schema_1; } +a.color-2-dark:hover { color:$color_schema_2; } +a.color-3-dark:hover { color:$color_schema_3; } +a.color-4-dark:hover { color:$color_schema_4; } +a.color-5-dark:hover { color:$color_schema_5; } + + +a.linkbox li { } + + +ul.linklist { + max-width:100%; + padding:0; + margin:5px; +} +ul.linklist li { + list-style:none; + +} +ul.linklist li a i{ + vertical-align:middle; + margin-right:6px; + font-size:2em;} + +ul.linklist > li a.linkbox { + border-radius: 0; + border: $box-border; +/* border-color:white;*/ + padding-top: 7px; + margin-bottom:0px; + margin-top:0px; + +/* border-bottom: $box-border; + border-top: $box-border; +*/ + border-color:white; +/* border-bottom-color:white; */ + +} +ul.linklist > li a.linkbox:hover { + + border: $box-border; + border-top: $box-border; + box-shadow: none; + border-color: black; + background: #FCFCFC; +/* margin: 0; + margin-bottom:0px;*/ +} + + +ul.linklist > li:first-child a.linkbox {} + +ul.linklist > li:last-child a.linkbox { +} + +ul.linklist > li:first-child a.linkbox { + border-top-left-radius:$box-border-radius; + border-top-right-radius:$box-border-radius; +/* border-top:$box-border; */ + margin-top:0px; +} + +ul.linklist > li:last-child a.linkbox { + border-bottom-left-radius:$box-border-radius; + border-bottom-right-radius:$box-border-radius; +/* border-bottom:$box-border;*/ +margin-bottom:0px; +} + +ul.linklist { + border-radius: $box-border-radius; + border: $box-border; +/* box-shadow: 0px 0px 1px 1px lightgray;*/ +} + + + + +span.linklist li a { + padding:5px; + border: $box-border; + background: $box-background; + display:block; + float:left; + +} + diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index b88554a..881e9a1 100755 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -16,7 +16,13 @@ end end - I18n.locale = session[:locale] || request.env['HTTP_ACCEPT_LANGUAGE'].scan(/^de|en/).first || I18n.default_locale + http_header=request.env['HTTP_ACCEPT_LANGUAGE'] + unless http_header.nil? + ht= http_header.scan(/^de|en/).first + else + ht=nil + end + I18n.locale = session[:locale] || ht || I18n.default_locale session[:locale] = I18n.locale @@ -46,7 +52,8 @@ else flash[:error] = "You must first login to view this page" session[:user_return_to] = request.url - redirect_to "/users/sign_in" + return redirect_to "/users/sign_in" + end end diff --git a/app/controllers/documents_controller.rb b/app/controllers/documents_controller.rb index bacf9af..22361db 100644 --- a/app/controllers/documents_controller.rb +++ b/app/controllers/documents_controller.rb @@ -157,7 +157,7 @@ class DocumentsController < ApplicationController @results = Document.search do fulltext params['query'] do fields :name, :text, :meeting - highlight :text + highlight :text,:name,:meeting end end @res=[] diff --git a/app/controllers/emails_controller.rb b/app/controllers/emails_controller.rb new file mode 100644 index 0000000..a10e2b6 --- /dev/null +++ b/app/controllers/emails_controller.rb @@ -0,0 +1,22 @@ +class EmailsController < ApplicationController +before_filter :authorize + + def index + + end + def daily_reminder + ActionMailer::Base.default_url_options[:host] = request.host_with_port + @mail =NewsMailer.daily_reminder(current_user.id) + if params[:send] + usr=User.where(:flag_getemails => true) + usr.each do |u| + NewsMailer.daily_reminder(current_user.id).deliver + end + end + render layout: false +end + protected + def authorize + authorize! :emails, User + end +end diff --git a/app/controllers/fetprofiles_controller.rb b/app/controllers/fetprofiles_controller.rb index ae58913..d7d99a7 100644 --- a/app/controllers/fetprofiles_controller.rb +++ b/app/controllers/fetprofiles_controller.rb @@ -9,18 +9,17 @@ class FetprofilesController < ApplicationController @fetprofiles = Fetprofile.order(:vorname,:nachname) if params[:filter]== "all" @fetprofiles = Fetprofile.where(:active=>false).order(:nachname,:vorname) if params[:filter]== "notactive" - @gremientabs = Gremium.tabs - @toolbar_elements << {:hicon=>'icon-plus', :text=> I18n.t('profile.new_profile'),:path => new_fetprofile_path(@fetprofile) } if can? :new, @fetprofile + @toolbar_elements << {:hicon=>'icon-plus', :text=> I18n.t('profile.new_profile'),:path => new_fetprofile_path(@fetprofile) } if can? :new, @fetprofile respond_to do |format| format.html # index.html.erb - end + end end def internlist @fetprofiles = Fetprofile.order(:vorname,:nachname) - end + end # GET /fetprofiles/1 # GET /fetprofiles/1.json diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb index d177155..28fd237 100755 --- a/app/controllers/home_controller.rb +++ b/app/controllers/home_controller.rb @@ -19,6 +19,11 @@ class HomeController < ApplicationController end def dev + end + def infoscreen + authorize! :doadmin, User + @neuigkeiten = Neuigkeit.accessible_by(current_ability, :show).where(flag_infoscreen: true) + render layout: false end def kontakt t=YAML.load_file("#{::Rails.root.to_s}/config/contact_topic.yml") diff --git a/app/controllers/neuigkeiten_controller.rb b/app/controllers/neuigkeiten_controller.rb index 4c117d3..ce08d18 100755 --- a/app/controllers/neuigkeiten_controller.rb +++ b/app/controllers/neuigkeiten_controller.rb @@ -3,6 +3,7 @@ class NeuigkeitenController < ApplicationController before_filter :load_toolbar_elements, :only=>[:show,:find_link] before_filter :load_toolbar_elements_edit, :only=>[:edit] + acts_as_flagable @@ -119,12 +120,15 @@ end end end def mail_preview + ActionMailer::Base.default_url_options[:host] = request.host_with_port + @neuigkeit = Neuigkeit.find(params[:id]) - @user=current_user + @user=current_user @ability=Ability.new(@user) + @mail = NewsMailer.neuigkeit_mail("all@fet.at", params[:id]) authorize! :publish, @neuigkeit - render template: "news_mailer/neuigkeit_mail", layout: false + render layout: false end def newsletter_preview authorize! :publish, Neuigkeit diff --git a/app/controllers/rubriken_controller.rb b/app/controllers/rubriken_controller.rb index b6c5e0e..966edf1 100755 --- a/app/controllers/rubriken_controller.rb +++ b/app/controllers/rubriken_controller.rb @@ -16,7 +16,7 @@ class RubrikenController < ApplicationController @rubriken= Rubrik.accessible_by(current_ability, :show) @neuigkeiten = Neuigkeit.accessible_by(current_ability, :list).page(params[:page]).per(3) - @calentries= (@rubriken.map {|r| r.calendar}).collect(&:calentries).flatten.select {|c| c.object !=nil} + @calentries= (@rubriken.map {|r| r.calendar.calentries.of_month(Date.new(params[:year],params[:month],1))}).flatten.select {|c| c.object !=nil} respond_to do |format| format.html format.js {render action: :show} diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 1e73890..4379695 100755 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -1,8 +1,22 @@ class UsersController < ApplicationController def index + authorize!(:index,User) @users = User.all end + def show + @user=User.find(params[:id]) + authorize!(:show, @user) + + end + def set_preferred_theme + @user=User.find(params[:id]) + authorize!(:set_preferred_theme, @user) + @user.preferredtheme=params[:theme] + @user.save + redirect_to root_url + end def add_role + authorize!(:add_role, User) @user= User.find(params[:id]) if (params[:role]=="fetuser" && can?(:addfetuser,User)) @user.add_role(params[:role]) @@ -13,6 +27,8 @@ class UsersController < ApplicationController redirect_to users_url end def fb_set_default_publish_page + authorize!(:doadmin, User) + if Fetsite::Application.config.facebookconfig_enabled if params["page"].nil? || !(current_user.provider=="facebook") redirect_to intern_home_index_path @@ -27,6 +43,7 @@ class UsersController < ApplicationController end def all_update + authorize! :doadmin,User params[:users].each do |id,u| user=User.find(id) user.fetprofile = Fetprofile.find(u[:fetprofile_id].to_i) if u[:fetprofile_id].to_i>0 diff --git a/app/mailers/news_mailer.rb b/app/mailers/news_mailer.rb index 311708e..86ee2be 100644 --- a/app/mailers/news_mailer.rb +++ b/app/mailers/news_mailer.rb @@ -1,23 +1,44 @@ +# -*- coding: utf-8 -*- + class NewsMailer < ActionMailer::Base default from: "salzamt@fet.at" helper :plugins - def current_user - @user - end + def neuigkeit_mail(email, neuigkeit_id) @neuigkeit= Neuigkeit.find(neuigkeit_id) @user=User.first @ability=Ability.new(@user) subject = @neuigkeit.title - subject = subject + " email: " + email if Rails.env=="development" + subject = subject.to_s + " email: " + email.to_s if Rails.env=="development" email = "andis@fet.at" if Rails.env=="development" email="andis@fet.at" mail(to: email, subject: subject) -render locals: {current_user: User.first} + render locals: {current_user: User.first} end def daily_newsletter(user_id) user=User.find(user_id) + self.message.perform_deliveries = false unless user.flag_getemails ability= Ability.new(user) + @neuigkeiten=Neuigkeit.accessible_by(ability).published_scope.where("cache_order<2") end + + def daily_reminder(user_id) + user=User.find(user_id) + @user=user + self.message.perform_deliveries = false unless user.flag_getemails + @ability = Ability.new(user) + @calentries = Calentry.of_month(Date.today).limit(100) +# @neuigkeiten=Neuigkeit.accessiblße_by(ability).published_scope.where("cache_order<2") + mail(to: user.email, content_type: "text/html", subject: "sdf") + render locals: {c_user: user} + end +private +include CanCan::ControllerAdditions + def current_user + @user + end + def current_ability + @ability + end end diff --git a/app/models/ability.rb b/app/models/ability.rb index 6dfe69d..99ba5bb 100755 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -152,6 +152,7 @@ end can :show, Neuigkeit, :cache_is_published=>true, :rubrik=>{:public=>true} if loggedin + can :show, :id => user.id end if( user.has_role?("fetuser") || user.has_role?("fetadmin")) @@ -159,6 +160,7 @@ end can :showintern, Neuigkeit can :showintern, Rubrik can :seeintern, User + can :set_preferred_theme, user can :list, Neuigkeit can :shownonpublic, Rubrik can :manage, Nlink @@ -169,8 +171,12 @@ end if user.has_role?("fetadmin") can :addfetuser, User can :addfetadmin, User + can :add_role, User + can :show, User + can :index, User can :edit, User can :manage, User + can :set_preferred_theme, User end if user.has_role?("newsadmin") || user.has_role?( "fetadmin") || user.has_role?( "fetuser") @@ -227,7 +233,8 @@ end can :doadmin, User end - unless user.has_role?("fetadmin") + if user.has_role?("fetadmin") + can :emails,User end end diff --git a/app/models/calentry.rb b/app/models/calentry.rb index b0908df..6421632 100644 --- a/app/models/calentry.rb +++ b/app/models/calentry.rb @@ -14,14 +14,14 @@ class Calentry < ActiveRecord::Base attr_accessible :ende, :start, :summary, :typ,:calendar_ids, :calendar, :dauer, :object_id, :object_type - belongs_to :calendar + belongs_to :calendar, touch: true #belongs_to :neuigkeit validates :start, :presence => true validates :object, :presence => true validates :typ, :presence => true before_save :get_public belongs_to :object, polymorphic: true, touch: true # Objekt zu dem der Calentry gehört (derzeit ein Newsartikel) - + scope :of_month, ->(d) {where("start >= ? AND start <= ?", d.beginning_of_month, d.end_of_month)} scope :upcoming, ->{ where("start >= ?", Time.now).order(:start)} scope :recent,-> { where("start <= ?", Time.now).order(:start).reverse_order} validate do |entry| @@ -31,7 +31,10 @@ class Calentry < ActiveRecord::Base end resourcify - + def self.updated_at + #Calentry.select("MAX(updated_at) as m_updated_at").first.m_updated_at + Calentry.all.max_by(&:updated_at).updated_at + end def is_upcomming? self.start > Time.now end diff --git a/app/models/document.rb b/app/models/document.rb index f820ebc..5123b52 100644 --- a/app/models/document.rb +++ b/app/models/document.rb @@ -102,7 +102,7 @@ class Document < ActiveRecord::Base end text :name, :boost=>4.0, :stored=> true text :meeting, stored: true do |d| - (d.parent_type == "Meeting")? d.try(:parent).try(:text).to_s : "" + (d.parent.is_a?(Meeting))? d.try(:parent).try(:text).to_s : "" end end def maketoc diff --git a/app/models/fetprofile.rb b/app/models/fetprofile.rb index 804995e..fbabb4d 100644 --- a/app/models/fetprofile.rb +++ b/app/models/fetprofile.rb @@ -32,13 +32,13 @@ class Fetprofile < ActiveRecord::Base has_many :nlinks, as: :link def validate_birthday unless birth_month.nil? || birth_day.nil? - unless Date.valid_date?((birth_year.nil?) ? Date.today.year : birth_year, birth_month, birth_day) - errors.add(:birth_month, "Invalides Datum") - errors.add(:birth_day, "Invalides Datum") - v= false - else - v= true - end + unless Date.valid_date?((birth_year.nil?) ? Date.today.year : birth_year, birth_month, birth_day) + errors.add(:birth_month, "Invalides Datum") + errors.add(:birth_day, "Invalides Datum") + v= false + else + v= true + end else v= false end @@ -78,3 +78,5 @@ class Fetprofile < ActiveRecord::Base end end + + diff --git a/app/models/gremium.rb b/app/models/gremium.rb index 8fd3c7e..8ee1800 100644 --- a/app/models/gremium.rb +++ b/app/models/gremium.rb @@ -23,6 +23,7 @@ class Gremium < ActiveRecord::Base has_many :memberships, dependent: :destroy # Mitgliedschaften bei dem Gremium has_many :nlinks, as: :link, dependent: :destroy # scope :search, ->(query) {where("gremien.name like ? or gremien.desc like ?", "%#{query}%", "%#{query}%")} +# has_many :members, through: :memberships searchable do text :desc text :name, :boost=>4.0 diff --git a/app/models/neuigkeit.rb b/app/models/neuigkeit.rb index 6bab7a0..0db49de 100755 --- a/app/models/neuigkeit.rb +++ b/app/models/neuigkeit.rb @@ -30,6 +30,7 @@ class Neuigkeit < ActiveRecord::Base translates :title,:text, :versioning=>{:gem=>:paper_trail, :options=>{:fallbacks_for_empty_translations => true}} globalize_accessors :locales => [:de, :en], :attributes =>[:text,:title] mount_uploader :picture, PictureUploader + include IsCommentable default_scope order(:cache_order).includes(:calentries).includes(:title_pic) scope :recent, -> { published.limit(10)} @@ -44,6 +45,12 @@ class Neuigkeit < ActiveRecord::Base before_validation :sanitize after_save :update_cache attr_accessor :no_fallbacks + + acts_as_flagable + + FLAG_ICONS={"infoscreen" => "fa fa-desktop", "important"=>"fa fa-exclamation"} + FLAG_CONFIRM={"infoscreen" => "Neuigkeit am Infoscreen anzeigen"} + def globalize_fallbacks(locale) if self.no_fallbacks [locale] diff --git a/app/models/studium.rb b/app/models/studium.rb index 2355ae6..1fd5866 100755 --- a/app/models/studium.rb +++ b/app/models/studium.rb @@ -34,7 +34,7 @@ class Studium < ActiveRecord::Base has_many :lvas, :through=>:moduls has_many :semester, :dependent => :destroy has_many :attachments, :as=>:parent -validates :abkuerzung, :length=>{:maximum=>5}, :format=>{:with=>/^[a-zA-z]{0,5}$/} + validates :abkuerzung, :length=>{:maximum=>5}, :format=>{:with=>/^[a-zA-z]{0,5}$/} validates :typ, :inclusion => {:in => ["Bachelor","Master"] } validates :name, :uniqueness => true, :presence=>true validates :zahl, :presence=>true, :format=>{:with=>/^[0-9A-Z]{4,10}$/}, :uniqueness => true diff --git a/app/models/survey/answer.rb b/app/models/survey/answer.rb index a7d2c78..244bafe 100644 --- a/app/models/survey/answer.rb +++ b/app/models/survey/answer.rb @@ -1,5 +1,5 @@ class Survey::Answer < ActiveRecord::Base - belongs_to :choice, class_name: 'Survey::Choice' + belongs_to :choice, class_name: 'Survey::Choice', touch: true belongs_to :user # attr_accessible :title, :body end diff --git a/app/models/survey/choice.rb b/app/models/survey/choice.rb index 22f8dbf..7fca518 100644 --- a/app/models/survey/choice.rb +++ b/app/models/survey/choice.rb @@ -1,5 +1,5 @@ class Survey::Choice < ActiveRecord::Base - belongs_to :question, class_name: 'Survey::Question' + belongs_to :question, class_name: 'Survey::Question', touch: true attr_accessible :picture, :sort, :text, :icon, :picture_cache, :remove_picture, :question_id has_many :answers, class_name: 'Survey::Answer', dependent: :destroy include ActionView::Helpers::TagHelper diff --git a/app/models/survey/question.rb b/app/models/survey/question.rb index 9500d05..b10c199 100644 --- a/app/models/survey/question.rb +++ b/app/models/survey/question.rb @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- class Survey::Question < ActiveRecord::Base attr_accessible :text, :title, :typ, :choice_ids, :parent_type, :parent_id - belongs_to :parent, polymorphic: true + belongs_to :parent, polymorphic: true, touch: true has_many :choices, dependent: :destroy, class_name: "Survey::Choice" has_many :answers, through: :choices include IsCommentable diff --git a/app/views/beispiele/_form.html.erb b/app/views/beispiele/_form.html.erb index 7f6df24..07285a6 100755 --- a/app/views/beispiele/_form.html.erb +++ b/app/views/beispiele/_form.html.erb @@ -12,7 +12,7 @@ <% end %>
- <% f.file_field :beispieldatei, :label => "Neue Datei hochladen:"%> + <%= f.file_field :beispieldatei, :label => "Neue Datei hochladen:" if @beispiel.beispieldatei.nil? || @beispiel.beispieldatei.blank? %>
<%=f.input :datum , :as => :datetimepicker %> @@ -20,11 +20,11 @@
<%= f.input :name %> - + <%= f.input :lva if @beispiel.lva.nil?%> <%= f.input :desc %>
- <%= f.input :lecturer, :as=>:select, :collection => @beispiel.lva.lecturers %> + <%= f.input :lecturer, :as=>:select, :collection => ((@beispiel.lva.nil?|| @beispiel.lva.lecturers.empty?)? Lecturer.all : @beispiel.lva.lecturers) %>
<%= f.hidden_field :beispieldatei_cache %> <%= f.actions do %> diff --git a/app/views/calentries/_mail.html.erb b/app/views/calentries/_mail.html.erb new file mode 100644 index 0000000..9c75a7e --- /dev/null +++ b/app/views/calentries/_mail.html.erb @@ -0,0 +1 @@ +<%= mail.icon_text %> diff --git a/app/views/documents/_search_results.html.erb b/app/views/documents/_search_results.html.erb index 47d616a..1d72a50 100644 --- a/app/views/documents/_search_results.html.erb +++ b/app/views/documents/_search_results.html.erb @@ -1,17 +1,19 @@ <% unless search_results.nil? %> -<%= search_results %> +<%#= search_results %> + + <% else %>
  • - <%= link_to('Login', new_user_session_path) %> + <%= link_to('Login', new_user_session_path, id:"login_link") %>
  • <% end %> diff --git a/app/views/neuigkeiten/_mail.html.erb b/app/views/neuigkeiten/_mail.html.erb new file mode 100644 index 0000000..543eea2 --- /dev/null +++ b/app/views/neuigkeiten/_mail.html.erb @@ -0,0 +1,10 @@ +

    + <%= mail.title%> +

    +<%= content_tag(:img,"",src: "http://" + ActionMailer::Base.default_url_options[:host].to_s + mail.picture.big_thumb.url.to_s) %> +<%=url_for controller: "neuigkeiten",action: :show, :id=>mail.id, only_path: false %> +<%= raw(mail.text) %> +<% mail.questions.each do |q| %> +<%= render partial:"survey/questions/mail", object: q %> +<% end %> +<%= link_to "Auf Fet.at weiterlesen", rubrik_neuigkeit_url(mail.rubrik, mail,:locale=>:de, :theme=>nil) %> diff --git a/app/views/neuigkeiten/_neuigkeit_view.html.erb b/app/views/neuigkeiten/_neuigkeit_view.html.erb index f29e612..3d9a78e 100644 --- a/app/views/neuigkeiten/_neuigkeit_view.html.erb +++ b/app/views/neuigkeiten/_neuigkeit_view.html.erb @@ -16,7 +16,7 @@ <% end %>
    <% unless neuigkeit_view.published? %> -
    Not Published <%= link_to "Publish", publish_rubrik_neuigkeit_path(@neuigkeit.rubrik,@neuigkeit),remote:true, class: :btn if can? :publish, neuigkeit_view %>
    +
    Not Published <%= link_to "Publish", publish_rubrik_neuigkeit_path(neuigkeit_view.rubrik,neuigkeit_view),remote:true, class: :btn if can? :publish, neuigkeit_view %>
    <% end %> <% unless neuigkeit_view.origurl.nil? || neuigkeit_view.origurl.empty? %>
    <%= link_to "Zitiert von "+ neuigkeit_view.origurl, neuigkeit_view.origurl %> diff --git a/app/views/neuigkeiten/mail_preview.html.erb b/app/views/neuigkeiten/mail_preview.html.erb new file mode 100644 index 0000000..31ad38f --- /dev/null +++ b/app/views/neuigkeiten/mail_preview.html.erb @@ -0,0 +1,5 @@ +<%= link_to "send", mail_preview_rubrik_neuigkeit_path(@neuigkeit) %> +<%= raw(@mail.subject) %> + +
    +<%= raw(@mail.body) %> diff --git a/app/views/news_mailer/daily_reminder.html.erb b/app/views/news_mailer/daily_reminder.html.erb new file mode 100644 index 0000000..468e1e9 --- /dev/null +++ b/app/views/news_mailer/daily_reminder.html.erb @@ -0,0 +1,13 @@ +<%= stylesheet_link_tag "http://"+ActionMailer::Base.default_url_options[:host].to_s + "/assets/themes/blue2/application.css", :media=>"all" %> + +<% @calentries.each do |ce| %> + +<% if can?(:show , ce.object) %> +
    +

    <%= render partial: "calentries/mail", object: ce %> +

    + <%= render partial: "neuigkeiten/mail", object: ce.object if ce.object.kind_of?(Neuigkeit)%> +<% end %> + +<% end %> +
    diff --git a/app/views/news_mailer/neuigkeit_mail.html.erb b/app/views/news_mailer/neuigkeit_mail.html.erb index b66792b..b1c2da5 100644 --- a/app/views/news_mailer/neuigkeit_mail.html.erb +++ b/app/views/news_mailer/neuigkeit_mail.html.erb @@ -4,7 +4,7 @@

    <%= @neuigkeit.title%>

    -<%= content_tag(:img,"",src: "http://"+ActionMailer::Base.default_url_options[:host].to_s+@neuigkeit.picture.big_thumb.url) %> +<%= content_tag(:img,"",src: "http://"+ActionMailer::Base.default_url_options[:host].to_s+@neuigkeit.picture.big_thumb.url.to_s) %> <%=url_for controller: "neuigkeiten",action: :show, :id=>@neuigkeit.id, only_path: false %> <%= raw(@neuigkeit.text) %> <% @neuigkeit.questions.each do |q| %> diff --git a/app/views/themes/blue2/fetprofiles/show.html.erb b/app/views/themes/blue2/fetprofiles/show.html.erb new file mode 100644 index 0000000..26180a8 --- /dev/null +++ b/app/views/themes/blue2/fetprofiles/show.html.erb @@ -0,0 +1,77 @@ +<%= content_for :header do %> +FET - <%= @fetprofile.name %> +<% unless @fetprofile.picture.url.nil? + picture_url=URI(root_url) + picture_url.path=@fetprofile.picture.portrait.url +end +%> + +<% set_meta_tags :og => { + :image => picture_url.to_s, + :title => @fetprofile.name.to_s , + :type => "profile", + :url=>fetprofile_url(@fetprofile,:theme=>nil), +:profile=> { + :first_name=>@fetprofile.vorname, + :last_name=>@fetprofile.nachname + } +} +%> +<% set_meta_tags :og=>{:article=> {:published_time=>I18n.l(@neuigkeit.try(:datum).try(:to_date)) }} unless @neuigkeit.try(:datum).try(:to_date).nil? %> + +<%= display_meta_tags %> +<% end %> + + +<%= render 'fetprofiles/tabs' %> + +
    + +

    <%= notice %>

    +
    + + +
    + + + <%= image_tag @fetprofile.picture.portrait.url %> + + + + + +
    +

    <%= @fetprofile.vorname %> <%= @fetprofile.nachname %><% unless @fetprofile.short.empty? %> <%= @fetprofile.short %> <% end %>

    + + +

    + <%= @fetprofile.fetmail %> +

    +

    + <%= @fetprofile.desc %> +

    + <%= render partial: "interninfo", object: @fetprofile if can?(:seeintern, @fetprofile) %> + +
      + <% @memberships.each do |m| %> +
    • + <%= link_to gremium_path(m.gremium) do %> <%= render m %><% end %> + <%= link_to I18n.t('common.edit'), edit_fetprofile_membership_path(@fetprofile,m) if params["verwalten"] && can?(:edit, m)%> + <%= link_to I18n.t('common.delete'), [@fetprofile, m], method: :delete, data: {confirm: I18n.t('common.sure_del')} if params["verwalten"] && can?(:delete, m ) %> +
    • + <% end %> +
    + +
    +
    +
    +<% if can?(:edit, @fetprofile) %> +
    + + <%= link_to ff_icon("icon-pencil")+I18n.t('common.edit'), edit_fetprofile_path(@fetprofile) if can?(:edit, @fetprofile) %> + <%= link_to fa_icon("trash")+I18n.t('common.delete'), fetprofile_path(@fetprofile),method: :delete, data: { confirm: "Are you sure?" } if can?(:delete, @fetprofile) %> + +
    +<% end %> + <%#= render 'layouts/pretty_toolbar' %> +
    diff --git a/app/views/themes/blue2/home/index.html.erb b/app/views/themes/blue2/home/index.html.erb index 481b871..0b9527b 100644 --- a/app/views/themes/blue2/home/index.html.erb +++ b/app/views/themes/blue2/home/index.html.erb @@ -46,7 +46,7 @@
    -