]> _ Git - fluidbook-toolbox.git/commitdiff
done #3458 @2.5
authorVincent Vanwaelscappel <vincent@cubedesigners.com>
Wed, 4 Mar 2020 18:06:06 +0000 (19:06 +0100)
committerVincent Vanwaelscappel <vincent@cubedesigners.com>
Wed, 4 Mar 2020 18:06:06 +0000 (19:06 +0100)
app/Http/Controllers/Admin/Operations/DownloadOperation.php
app/Http/Controllers/Admin/Operations/PreviewOperation.php
app/Models/Quiz.php
app/Models/QuizTranslation.php
composer.json
config/backpack/base.php
public/packages/featherlight/featherlight.min.css [new file with mode: 0644]
public/packages/featherlight/featherlight.min.js [new file with mode: 0644]
resources/quiz/banner.jpg [new file with mode: 0644]
resources/quiz/template.zip [new file with mode: 0644]

index 60f82043ba7ec19889cd069a674f8ba700cd7007..5854ee6d7fb32fbbce2f82314df443938a63247a 100644 (file)
@@ -11,8 +11,10 @@ trait DownloadOperation
         Route::match(['get'], $segment . '/{id}/download', $controller . '@download');
     }
 
-    protected function download()
+    protected function download($id)
     {
-        return ';))';
+        $id = $this->crud->getCurrentEntryId() ?? $id;
+        $entry=$this->crud->getEntry($id);
+        $entry->compile();
     }
 }
index 8a11e0723344ab5e0f73a36644cd6a108ab9da6f..9fa48e81286a660c3bf94344c95a2b4c2a367093 100644 (file)
@@ -2,16 +2,27 @@
 
 namespace App\Http\Controllers\Admin\Operations;
 
+use Cubist\Util\Files\Files;
 use Illuminate\Support\Facades\Route;
 
 trait PreviewOperation
 {
     protected function setupPreviewRoutes($segment, $routeName, $controller)
     {
-        Route::match(['get'], $segment . '/{id}/preview', $controller . '@preview');
+        Route::match(['get'], $segment . '/{id}/preview/{path?}', $controller . '@preview')
+            ->where(['id' => '[0-9]+', 'path' => '.*']);
     }
 
-    protected function preview(){
-        return ':)';
+    protected function preview($id, $path = 'index.html')
+    {
+        $dest = protected_path('quiz/final/' . $id);
+
+        if ($path === 'index.html') {
+            $entry = $this->crud->getEntry($id);
+            $entry->compile($dest);
+        }
+
+        $p = $dest . '/' . $path;
+        return response(null)->header('Content-Type', Files::_getMimeType($p))->header('X-Sendfile', $p);
     }
 }
index ab39b96c2b825ba2956f4f76a15ca8fd2b53cc72..283390e0b5f955928d8501db0a66438431c59c6f 100644 (file)
@@ -6,6 +6,9 @@ namespace App\Models;
 use App\Http\Controllers\Admin\Base\QuizController;
 use Backpack\CRUD\app\Library\CrudPanel\CrudPanel;
 use Cubist\Backpack\app\Magic\Models\CubistMagicAbstractModel;
+use Cubist\Util\Files\Files;
+use Cubist\Util\Zip;
+use Spatie\MediaLibrary\Models\Media;
 
 class Quiz extends CubistMagicAbstractModel
 {
@@ -17,6 +20,27 @@ class Quiz extends CubistMagicAbstractModel
         'singular' => 'quiz',
         'plural' => 'quizzes'];
 
+    protected static $_messages = ['defaultMessage' => 'Message displayed at the end of the quiz',
+        'passedMessage' => 'Message displayed when passing the quiz',
+        'failedMessage' => 'Message displayed when failing the quiz'];
+
+    protected static $_colors = [
+        'mainColor' => ['label' => 'Main color', 'hint' => 'Buttons and rollovers color', 'value' => '#e7007a'],
+        'overlay' => ['label' => 'Overlay', 'hint' => 'Overlay set on the banner', 'value' => 'rgba(0,0,0,0.75)'],
+        'reviewBackground' => ['label' => 'Review panel background color', 'value' => '#262626'],
+        'okColor' => ['label' => 'OK color', 'hint' => 'Color of the ok badge (usually green)', 'value' => '#66c924'],
+        'nokColor' => ['label' => 'NOK color', 'hint' => 'Color of the not ok badge (usually red)', 'value' => '#c61e35'],
+    ];
+
+    protected static $_images = [
+        'logo' => 'Logo',
+        'banner' => 'Banner',
+    ];
+
+    protected static $_actions = [
+        'passedAction' => 'Run code when passing the quiz',
+        'failedAction' => 'Run code when failing the quiz'];
+
     /**
      * @param $crud CrudPanel
      */
@@ -30,7 +54,7 @@ class Quiz extends CubistMagicAbstractModel
 
     public function openPreviewButton($crud = false)
     {
-        return '<a class="btn btn-sm btn-link" target="_blank" href="' . url($crud->route . '/' . $this->getKey() . '/preview') . '" data-toggle="tooltip" title="Preview quiz"><i class="fa fa-eye"></i> Preview</a>';
+        return '<a class="btn btn-sm btn-link iframe" data-featherlight="iframe" data-featherlight-iframe-width="800" data-featherlight-iframe-height="600" target="_blank" href="' . url($crud->route . '/' . $this->getKey() . '/preview/index.html') . '" data-toggle="tooltip" title="Preview quiz"><i class="fa fa-eye"></i> Preview</a>';
     }
 
     public function downloadButton($crud = false)
@@ -56,11 +80,8 @@ class Quiz extends CubistMagicAbstractModel
             'column' => true,
             'tab' => 'Contents']);
 
-        $messages = ['defaultMessage' => 'Message displayed at the end of the quiz',
-            'passedMessage' => 'Message displayed when passing the quiz',
-            'failedMessage' => 'Message displayed when failing the quiz'];
 
-        foreach ($messages as $name => $label) {
+        foreach (self::$_messages as $name => $label) {
             $this->addField(['name' => $name,
                 'label' => $label,
                 'hint' => 'Let this field empty to use the default message',
@@ -97,41 +118,109 @@ class Quiz extends CubistMagicAbstractModel
             'type' => 'Checkbox',
             'tab' => 'Settings']);
 
-        $this->addField(['name' => 'passedAction',
-            'label' => 'Run code when passing the quiz',
-            'hint' => 'JavaScript code',
-            'type' => 'Textarea',
-            'tab' => 'Settings']);
-
-        $this->addField(['name' => 'failedAction',
-            'label' => 'Run code when failing the quiz',
-            'hint' => 'JavaScript code',
-            'type' => 'Textarea',
-            'tab' => 'Settings']);
 
-        $colors = [
-            'mainColor' => ['label' => 'Main color', 'hint' => 'Buttons and rollovers color', 'value' => '#e7007a'],
-            'overlay' => ['label' => 'Overlay', 'hint' => 'Overlay set on the banner', 'value' => 'rgba(0,0,0,0.75)'],
-            'reviewBackground' => ['label' => 'Review panel background color', 'value' => '#262626'],
-            'okColor' => ['label' => 'OK color', 'hint' => 'Color of the ok badge (usually green)', 'value' => '#66c924'],
-            'nokColor' => ['label' => 'NOK color', 'hint' => 'Color of the not ok badge (usually red)', 'value' => '#c61e35'],
-        ];
+        foreach (self::$_actions as $action => $label) {
+            $this->addField(['name' => $action,
+                'label' => $label,
+                'hint' => 'JavaScript code',
+                'type' => 'Textarea',
+                'tab' => 'Settings']);
+        }
 
         $default = ['tab' => 'Theme', 'type' => 'Color'];
-        foreach ($colors as $name => $color) {
+        foreach (self::$_colors as $name => $color) {
             $f = array_merge($default, $color, ['name' => $name]);
             $this->addField($f);
         }
 
-        $images = [
-            'logo' => 'Logo',
-            'banner' => 'Banner',
-        ];
+
         $default = ['tab' => 'Theme', 'type' => 'Images', 'maxFiles' => 1];
-        foreach ($images as $name => $label) {
+        foreach (self::$_images as $name => $label) {
             $f = array_merge($default, ['name' => $name, 'label' => $label]);
             $this->addField($f);
         }
 
     }
+
+    public function registerMediaConversions(Media $media = null)
+    {
+        parent::registerMediaConversions($media);
+
+        $this->addMediaConversion('logo_compile')->width(300)->height(100)->format('png');
+        $this->addMediaConversion('banner_compile')->width(1920)->height(600)->format('jpg');
+    }
+
+    /**
+     * @param $quiz self
+     */
+    public function compile($dest)
+    {
+        // Clean existing dir and recreate it
+        if (file_exists($dest)) {
+            Files::rmdir($dest);
+        }
+        mkdir($dest, 0777, true);
+
+        // Extract template into the final dir
+        $template = resource_path('quiz/template.zip');
+        Zip::extract($template, $dest);
+
+        // Copy assets
+        $assets = ['banner' => 'banner.jpg', 'logo' => 'logo.png'];
+        foreach ($assets as $asset => $filename) {
+            $coll = $this->getAttribute($asset);
+            if ($coll) {
+                $media = $this->getFirstMedia($coll);
+                if ($media) {
+                    $path = $media->getPath($asset . '_compile');
+                    copy($path, $dest . '/assets/' . $filename);
+                }
+            }
+        }
+
+        // Create data.xml
+        $xml = simplexml_load_string('<?xml version="1.0" encoding="UTF-8" ?><quiz />');
+        $xml->addAttribute('id', $this->getAttribute('id'));
+        $xml->addChild('title', $this->getAttribute('title'));
+        $xml->addChild('threshold', $this->getAttribute('threshold') ?: '0');
+        $xml->addChild('review', $this->getAttribute('review') ? '1' : '0');
+        $xt = $xml->addChild('translations');
+        /** @var QuizTranslation $translation */
+        $translation = QuizTranslation::find($this->getAttribute('translation'));
+        foreach (QuizTranslation::getTexts() as $text => $default) {
+            $xt->addChild($text, $translation->getAttribute($text));
+        }
+        foreach (self::$_colors as $color => $c) {
+            $xml->addChild($color, $this->getAttribute($color));
+        }
+        foreach (self::$_messages as $message => $m) {
+            $xml->addChild($message, $this->getAttribute($message) ?: $translation->getAttribute($message));
+        }
+        foreach (self::$_actions as $action => $a) {
+            $xml->addChild($action, $this->getAttribute($action));
+        }
+        $questions = $this->getAttribute('questions');
+        $xqs = $xml->addChild('questions');
+        foreach ($questions as $question) {
+            $xq = $xqs->addChild('question');
+            if ($question['multiple']) {
+                $xq->addAttribute('multiple', '1');
+            }
+            $xq->addChild('label', $question['question']);
+            $xas = $xq->addChild('answers');
+            foreach ($question['answers'] as $answer) {
+                $xa = $xas->addChild('answer', $answer['answer']);
+                if ($answer['correct']) {
+                    $xa->addAttribute('correct', '1');
+                }
+            }
+            $xq->addChild('correction', $question['explaination']);
+        }
+        file_put_contents($dest . '/data.xml', tidy_repair_string($xml->asXML(), ['input-xml' => 1, 'indent' => 1, 'wrap' => 0]));
+    }
+
+    public static function getMessages()
+    {
+        return self::$_messages;
+    }
 }
index 062ee8539199869b527f5c372a8dd2d51143facd..bc9be5fedcb83d2cfc17608fea3811b33678371f 100644 (file)
@@ -11,36 +11,36 @@ class QuizTranslation extends CubistMagicAbstractModel
         'singular' => 'quiz translation',
         'plural' => 'quiz translations'];
 
+    protected static $_texts= ['validateAnswer' => 'Validate answers',
+        'reviewAnswer' => 'Review answers',
+        'question' => 'Question %d:',
+        'totalQuestions' => 'Total questions:',
+        'correctAnswers' => 'Correct answers:'];
+
     public function setFields()
     {
         parent::setFields();
 
-        $texts = ['validateAnswer' => 'Validate answers',
-            'reviewAnswer' => 'Review answers',
-            'question' => 'Question %d:',
-            'totalQuestions' => 'Total questions:',
-            'correctAnswers' => 'Correct answers:'];
-
         $this->addField(['name' => 'locale',
             'label' => 'Language',
             'type' => 'Locale',
             'column' => true]);
 
-        foreach ($texts as $name => $default) {
+        foreach (self::$_texts as $name => $default) {
             $this->addField(['name' => $name,
                 'label' => 'Translation of « ' . $default . ' »',
                 'type' => 'Text']);
         }
 
-        $messages = ['defaultMessage' => 'Message displayed at the end of the quiz',
-            'passedMessage' => 'Message displayed when passing the quiz',
-            'failedMessage' => 'Message displayed when failing the quiz'];
-
-        foreach ($messages as $name => $label) {
+        foreach (Quiz::getMessages() as $name => $label) {
             $this->addField([
                 'name' => $name,
                 'label' => $label,
                 'type' => 'Text']);
         }
     }
+
+    public static function getTexts(){
+        return self::$_texts;
+    }
 }
index 352d604bc8f22bf6657345663999d31d55619d41..b40c28146a168fe3c6b377599af19adf5c0ef642 100644 (file)
@@ -16,7 +16,8 @@
     "require": {
         "php": ">=7.4",
         "cubist/cms-back": "dev-master",
-        "league/csv": "^9.5"
+        "league/csv": "^9.5",
+        "ext-tidy": "*"
     },
     "require-dev": {
         "facade/ignition": "^1.4",
index 682ab435b2dba5a58490e77543613ea9530f075c..6d8b4f00ff30cfa37e7c771c4ccd8e3bc11cab19 100644 (file)
@@ -44,6 +44,7 @@ return [
     // CSS files that are loaded in all pages, using Laravel's asset() helper
     'styles' => [
         'packages/backpack/base/css/bundle.css',
+        'packages/featherlight/featherlight.min.css',
         'packages/cubist/fluidbooktoolbox/css/style.css',
 
         // Here's what's inside the bundle:
@@ -124,7 +125,9 @@ return [
     'scripts' => [
         // Backstrap includes jQuery, Bootstrap, CoreUI, PNotify, Popper
         'packages/backpack/base/js/bundle.js',
-        'packages/cubist/fluidbooktoolbox/js/bundle.css',
+        'packages/featherlight/featherlight.min.js',
+        'packages/cubist/fluidbooktoolbox/js/bundle.js',
+
 
         // examples (everything inside the bundle, loaded from CDN)
         // 'https://code.jquery.com/jquery-3.4.1.min.js',
diff --git a/public/packages/featherlight/featherlight.min.css b/public/packages/featherlight/featherlight.min.css
new file mode 100644 (file)
index 0000000..62fdb71
--- /dev/null
@@ -0,0 +1,8 @@
+/**
+ * Featherlight - ultra slim jQuery lightbox
+ * Version 1.7.14 - http://noelboss.github.io/featherlight/
+ *
+ * Copyright 2019, Noël Raoul Bossart (http://www.noelboss.com)
+ * MIT Licensed.
+**/
+html.with-featherlight{overflow:hidden}.featherlight{display:none;position:fixed;top:0;right:0;bottom:0;left:0;z-index:2147483647;text-align:center;white-space:nowrap;cursor:pointer;background:#333;background:rgba(0,0,0,0)}.featherlight:last-of-type{background:rgba(0,0,0,.8)}.featherlight:before{content:'';display:inline-block;height:100%;vertical-align:middle}.featherlight .featherlight-content{position:relative;text-align:left;vertical-align:middle;display:inline-block;overflow:auto;padding:25px 25px 0;border-bottom:25px solid transparent;margin-left:5%;margin-right:5%;max-height:95%;background:#fff;cursor:auto;white-space:normal}.featherlight .featherlight-inner{display:block}.featherlight link.featherlight-inner,.featherlight script.featherlight-inner,.featherlight style.featherlight-inner{display:none}.featherlight .featherlight-close-icon{position:absolute;z-index:9999;top:0;right:0;line-height:25px;width:25px;cursor:pointer;text-align:center;font-family:Arial,sans-serif;background:#fff;background:rgba(255,255,255,.3);color:#000;border:0;padding:0}.featherlight .featherlight-close-icon::-moz-focus-inner{border:0;padding:0}.featherlight .featherlight-image{width:100%}.featherlight-iframe .featherlight-content{border-bottom:0;padding:0;-webkit-overflow-scrolling:touch}.featherlight iframe{border:0}.featherlight *{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}@media only screen and (max-width:1024px){.featherlight .featherlight-content{margin-left:0;margin-right:0;max-height:98%;padding:10px 10px 0;border-bottom:10px solid transparent}}@media print{html.with-featherlight>*>:not(.featherlight){display:none}}
\ No newline at end of file
diff --git a/public/packages/featherlight/featherlight.min.js b/public/packages/featherlight/featherlight.min.js
new file mode 100644 (file)
index 0000000..3b53d66
--- /dev/null
@@ -0,0 +1,8 @@
+/**
+ * Featherlight - ultra slim jQuery lightbox
+ * Version 1.7.14 - http://noelboss.github.io/featherlight/
+ *
+ * Copyright 2019, Noël Raoul Bossart (http://www.noelboss.com)
+ * MIT Licensed.
+**/
+!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):"object"==typeof module&&module.exports?module.exports=function(b,c){return void 0===c&&(c="undefined"!=typeof window?require("jquery"):require("jquery")(b)),a(c),c}:a(jQuery)}(function(a){"use strict";function b(a,c){if(!(this instanceof b)){var d=new b(a,c);return d.open(),d}this.id=b.id++,this.setup(a,c),this.chainCallbacks(b._callbackChain)}function c(a,b){var c={};for(var d in a)d in b&&(c[d]=a[d],delete a[d]);return c}function d(a,b){var c={},d=new RegExp("^"+b+"([A-Z])(.*)");for(var e in a){var f=e.match(d);if(f){var g=(f[1]+f[2].replace(/([A-Z])/g,"-$1")).toLowerCase();c[g]=a[e]}}return c}if("undefined"==typeof a)return void("console"in window&&window.console.info("Too much lightness, Featherlight needs jQuery."));if(a.fn.jquery.match(/-ajax/))return void("console"in window&&window.console.info("Featherlight needs regular jQuery, not the slim version."));var e=[],f=function(b){return e=a.grep(e,function(a){return a!==b&&a.$instance.closest("body").length>0})},g={allow:1,allowfullscreen:1,frameborder:1,height:1,longdesc:1,marginheight:1,marginwidth:1,mozallowfullscreen:1,name:1,referrerpolicy:1,sandbox:1,scrolling:1,src:1,srcdoc:1,style:1,webkitallowfullscreen:1,width:1},h={keyup:"onKeyUp",resize:"onResize"},i=function(c){a.each(b.opened().reverse(),function(){return c.isDefaultPrevented()||!1!==this[h[c.type]](c)?void 0:(c.preventDefault(),c.stopPropagation(),!1)})},j=function(c){if(c!==b._globalHandlerInstalled){b._globalHandlerInstalled=c;var d=a.map(h,function(a,c){return c+"."+b.prototype.namespace}).join(" ");a(window)[c?"on":"off"](d,i)}};b.prototype={constructor:b,namespace:"featherlight",targetAttr:"data-featherlight",variant:null,resetCss:!1,background:null,openTrigger:"click",closeTrigger:"click",filter:null,root:"body",openSpeed:250,closeSpeed:250,closeOnClick:"background",closeOnEsc:!0,closeIcon:"&#10005;",loading:"",persist:!1,otherClose:null,beforeOpen:a.noop,beforeContent:a.noop,beforeClose:a.noop,afterOpen:a.noop,afterContent:a.noop,afterClose:a.noop,onKeyUp:a.noop,onResize:a.noop,type:null,contentFilters:["jquery","image","html","ajax","iframe","text"],setup:function(b,c){"object"!=typeof b||b instanceof a!=!1||c||(c=b,b=void 0);var d=a.extend(this,c,{target:b}),e=d.resetCss?d.namespace+"-reset":d.namespace,f=a(d.background||['<div class="'+e+"-loading "+e+'">','<div class="'+e+'-content">','<button class="'+e+"-close-icon "+d.namespace+'-close" aria-label="Close">',d.closeIcon,"</button>",'<div class="'+d.namespace+'-inner">'+d.loading+"</div>","</div>","</div>"].join("")),g="."+d.namespace+"-close"+(d.otherClose?","+d.otherClose:"");return d.$instance=f.clone().addClass(d.variant),d.$instance.on(d.closeTrigger+"."+d.namespace,function(b){if(!b.isDefaultPrevented()){var c=a(b.target);("background"===d.closeOnClick&&c.is("."+d.namespace)||"anywhere"===d.closeOnClick||c.closest(g).length)&&(d.close(b),b.preventDefault())}}),this},getContent:function(){if(this.persist!==!1&&this.$content)return this.$content;var b=this,c=this.constructor.contentFilters,d=function(a){return b.$currentTarget&&b.$currentTarget.attr(a)},e=d(b.targetAttr),f=b.target||e||"",g=c[b.type];if(!g&&f in c&&(g=c[f],f=b.target&&e),f=f||d("href")||"",!g)for(var h in c)b[h]&&(g=c[h],f=b[h]);if(!g){var i=f;if(f=null,a.each(b.contentFilters,function(){return g=c[this],g.test&&(f=g.test(i)),!f&&g.regex&&i.match&&i.match(g.regex)&&(f=i),!f}),!f)return"console"in window&&window.console.error("Featherlight: no content filter found "+(i?' for "'+i+'"':" (no target specified)")),!1}return g.process.call(b,f)},setContent:function(b){return this.$instance.removeClass(this.namespace+"-loading"),this.$instance.toggleClass(this.namespace+"-iframe",b.is("iframe")),this.$instance.find("."+this.namespace+"-inner").not(b).slice(1).remove().end().replaceWith(a.contains(this.$instance[0],b[0])?"":b),this.$content=b.addClass(this.namespace+"-inner"),this},open:function(b){var c=this;if(c.$instance.hide().appendTo(c.root),!(b&&b.isDefaultPrevented()||c.beforeOpen(b)===!1)){b&&b.preventDefault();var d=c.getContent();if(d)return e.push(c),j(!0),c.$instance.fadeIn(c.openSpeed),c.beforeContent(b),a.when(d).always(function(a){a&&(c.setContent(a),c.afterContent(b))}).then(c.$instance.promise()).done(function(){c.afterOpen(b)})}return c.$instance.detach(),a.Deferred().reject().promise()},close:function(b){var c=this,d=a.Deferred();return c.beforeClose(b)===!1?d.reject():(0===f(c).length&&j(!1),c.$instance.fadeOut(c.closeSpeed,function(){c.$instance.detach(),c.afterClose(b),d.resolve()})),d.promise()},resize:function(a,b){if(a&&b){this.$content.css("width","").css("height","");var c=Math.max(a/(this.$content.parent().width()-1),b/(this.$content.parent().height()-1));c>1&&(c=b/Math.floor(b/c),this.$content.css("width",""+a/c+"px").css("height",""+b/c+"px"))}},chainCallbacks:function(b){for(var c in b)this[c]=a.proxy(b[c],this,a.proxy(this[c],this))}},a.extend(b,{id:0,autoBind:"[data-featherlight]",defaults:b.prototype,contentFilters:{jquery:{regex:/^[#.]\w/,test:function(b){return b instanceof a&&b},process:function(b){return this.persist!==!1?a(b):a(b).clone(!0)}},image:{regex:/\.(png|jpg|jpeg|gif|tiff?|bmp|svg)(\?\S*)?$/i,process:function(b){var c=this,d=a.Deferred(),e=new Image,f=a('<img src="'+b+'" alt="" class="'+c.namespace+'-image" />');return e.onload=function(){f.naturalWidth=e.width,f.naturalHeight=e.height,d.resolve(f)},e.onerror=function(){d.reject(f)},e.src=b,d.promise()}},html:{regex:/^\s*<[\w!][^<]*>/,process:function(b){return a(b)}},ajax:{regex:/./,process:function(b){var c=a.Deferred(),d=a("<div></div>").load(b,function(a,b){"error"!==b&&c.resolve(d.contents()),c.reject()});return c.promise()}},iframe:{process:function(b){var e=new a.Deferred,f=a("<iframe/>"),h=d(this,"iframe"),i=c(h,g);return f.hide().attr("src",b).attr(i).css(h).on("load",function(){e.resolve(f.show())}).appendTo(this.$instance.find("."+this.namespace+"-content")),e.promise()}},text:{process:function(b){return a("<div>",{text:b})}}},functionAttributes:["beforeOpen","afterOpen","beforeContent","afterContent","beforeClose","afterClose"],readElementConfig:function(b,c){var d=this,e=new RegExp("^data-"+c+"-(.*)"),f={};return b&&b.attributes&&a.each(b.attributes,function(){var b=this.name.match(e);if(b){var c=this.value,g=a.camelCase(b[1]);if(a.inArray(g,d.functionAttributes)>=0)c=new Function(c);else try{c=JSON.parse(c)}catch(h){}f[g]=c}}),f},extend:function(b,c){var d=function(){this.constructor=b};return d.prototype=this.prototype,b.prototype=new d,b.__super__=this.prototype,a.extend(b,this,c),b.defaults=b.prototype,b},attach:function(b,c,d){var e=this;"object"!=typeof c||c instanceof a!=!1||d||(d=c,c=void 0),d=a.extend({},d);var f,g=d.namespace||e.defaults.namespace,h=a.extend({},e.defaults,e.readElementConfig(b[0],g),d),i=function(g){var i=a(g.currentTarget),j=a.extend({$source:b,$currentTarget:i},e.readElementConfig(b[0],h.namespace),e.readElementConfig(g.currentTarget,h.namespace),d),k=f||i.data("featherlight-persisted")||new e(c,j);"shared"===k.persist?f=k:k.persist!==!1&&i.data("featherlight-persisted",k),j.$currentTarget.blur&&j.$currentTarget.blur(),k.open(g)};return b.on(h.openTrigger+"."+h.namespace,h.filter,i),{filter:h.filter,handler:i}},current:function(){var a=this.opened();return a[a.length-1]||null},opened:function(){var b=this;return f(),a.grep(e,function(a){return a instanceof b})},close:function(a){var b=this.current();return b?b.close(a):void 0},_onReady:function(){var b=this;if(b.autoBind){var c=a(b.autoBind);c.each(function(){b.attach(a(this))}),a(document).on("click",b.autoBind,function(d){if(!d.isDefaultPrevented()){var e=a(d.currentTarget),f=c.length;if(c=c.add(e),f!==c.length){var g=b.attach(e);(!g.filter||a(d.target).parentsUntil(e,g.filter).length>0)&&g.handler(d)}}})}},_callbackChain:{onKeyUp:function(b,c){return 27===c.keyCode?(this.closeOnEsc&&a.featherlight.close(c),!1):b(c)},beforeOpen:function(b,c){return a(document.documentElement).addClass("with-featherlight"),this._previouslyActive=document.activeElement,this._$previouslyTabbable=a("a, input, select, textarea, iframe, button, iframe, [contentEditable=true]").not("[tabindex]").not(this.$instance.find("button")),this._$previouslyWithTabIndex=a("[tabindex]").not('[tabindex="-1"]'),this._previousWithTabIndices=this._$previouslyWithTabIndex.map(function(b,c){return a(c).attr("tabindex")}),this._$previouslyWithTabIndex.add(this._$previouslyTabbable).attr("tabindex",-1),document.activeElement.blur&&document.activeElement.blur(),b(c)},afterClose:function(c,d){var e=c(d),f=this;return this._$previouslyTabbable.removeAttr("tabindex"),this._$previouslyWithTabIndex.each(function(b,c){a(c).attr("tabindex",f._previousWithTabIndices[b])}),this._previouslyActive.focus(),0===b.opened().length&&a(document.documentElement).removeClass("with-featherlight"),e},onResize:function(a,b){return this.resize(this.$content.naturalWidth,this.$content.naturalHeight),a(b)},afterContent:function(a,b){var c=a(b);return this.$instance.find("[autofocus]:not([disabled])").focus(),this.onResize(b),c}}}),a.featherlight=b,a.fn.featherlight=function(a,c){return b.attach(this,a,c),this},a(document).ready(function(){b._onReady()})});
\ No newline at end of file
diff --git a/resources/quiz/banner.jpg b/resources/quiz/banner.jpg
new file mode 100644 (file)
index 0000000..75f2cd5
Binary files /dev/null and b/resources/quiz/banner.jpg differ
diff --git a/resources/quiz/template.zip b/resources/quiz/template.zip
new file mode 100644 (file)
index 0000000..0088f08
Binary files /dev/null and b/resources/quiz/template.zip differ