diff --git a/.github/workflows/test_js.yaml b/.github/workflows/test_js.yaml new file mode 100644 index 0000000..e605500 --- /dev/null +++ b/.github/workflows/test_js.yaml @@ -0,0 +1,34 @@ +name: JS Test multiselect + +on: + push: + paths: + - '**.js' + +jobs: + test: + name: TEST MULTISELECT + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Install + run: | + npm --save-dev install \ + qunit \ + karma \ + karma-qunit \ + karma-coverage \ + karma-chrome-launcher \ + karma-module-resolver-preprocessor + npm --no-save install https://github.com/jquery/jquery#main + + - name: Run tests + run: | + node_modules/karma/bin/karma start js/karma.conf.js + + - name: Run coverage + run: | + npm run karma-test:coverage diff --git a/.github/workflows/test_py.yaml b/.github/workflows/test_py.yaml new file mode 100644 index 0000000..3c76870 --- /dev/null +++ b/.github/workflows/test_py.yaml @@ -0,0 +1,52 @@ +name: Test multiselect + +on: + push: + paths: + - '**.py' + +jobs: + test: + name: Test ${{ matrix.python }} - ${{ matrix.os }} + runs-on: ${{ matrix.os }} + + strategy: + fail-fast: false + matrix: + os: + - ubuntu-latest + - windows-latest + - macos-latest + + python: + - "2.7" + - "3.7" + - "3.8" + - "3.9" + - "3.10" + + steps: + - uses: actions/checkout@v2 + + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python }} + + - name: Install + run: | + pip install wheel + pip install coverage + pip install lxml + pip install zope.testrunner + pip install https://github.com/conestack/webresource/archive/master.zip + pip install https://github.com/conestack/yafowil/archive/master.zip + pip install -e .[test] + - name: Run tests + run: | + python --version + python -m yafowil.widget.multiselect.tests + - name: Run coverage + run: | + coverage run --source=src/yafowil/widget/multiselect --omit=src/yafowil/widget/multiselect/example.py -m yafowil.widget.multiselect.tests + coverage report --fail-under=99 \ No newline at end of file diff --git a/.gitignore b/.gitignore index f5f6b26..904259c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,18 +1,12 @@ -*~ -*#* +*.egg-info *.pyc *.pyo -*.mo -*.egg-info -/develop-eggs/ -/parts/ -/bin/ -/eggs/ -/downloads/ -/var/ +/.coverage /dist/ -/.installed.cfg -/.pydevproject -/.project -/.DS_Store -/.mr.developer.cfg +/htmlcov/ +/js/karma/ +/node_modules/ +/package-lock.json +/py2/ +/py3/ +/pypy3/ diff --git a/CHANGES.rst b/CHANGES.rst index 0d8edb3..01146a4 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,10 +1,15 @@ Changes ======= -1.4 (unreleased) +2.0 (unreleased) ---------------- -- No changes yet. +- Extend JS by ``multiselect_on_array_add`` and ``register_array_subscribers`` + functions to enable usage in ``yafowil.widget.array``. + [lenadax] + +- Rewrite JavaScript using ES6. + [lenadax] 1.3 (2018-07-16) diff --git a/LICENSE.rst b/LICENSE.rst index 01c1de3..f1304c3 100644 --- a/LICENSE.rst +++ b/LICENSE.rst @@ -2,7 +2,7 @@ License ======= Copyright (c) 2012-2021, BlueDynamics Alliance, Austria, Germany, Switzerland -Copyright (c) 2021, Yafowil Contributors +Copyright (c) 2021-2022, Yafowil Contributors All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/README.rst b/README.rst index 150190e..43b2039 100644 --- a/README.rst +++ b/README.rst @@ -24,3 +24,5 @@ Contributors - Robert Niederreiter - Georg Bernhard + +- Lena Daxenbichler diff --git a/bootstrap.py b/bootstrap.py deleted file mode 100644 index f78186f..0000000 --- a/bootstrap.py +++ /dev/null @@ -1,170 +0,0 @@ -############################################################################## -# -# Copyright (c) 2006 Zope Foundation and Contributors. -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. -# -############################################################################## -"""Bootstrap a buildout-based project - -Simply run this script in a directory containing a buildout.cfg. -The script accepts buildout command-line options, so you can -use the -c option to specify an alternate configuration file. -""" - -import os -import shutil -import sys -import tempfile - -from optparse import OptionParser - -tmpeggs = tempfile.mkdtemp() - -usage = '''\ -[DESIRED PYTHON FOR BUILDOUT] bootstrap.py [options] - -Bootstraps a buildout-based project. - -Simply run this script in a directory containing a buildout.cfg, using the -Python that you want bin/buildout to use. - -Note that by using --find-links to point to local resources, you can keep -this script from going over the network. -''' - -parser = OptionParser(usage=usage) -parser.add_option("-v", "--version", help="use a specific zc.buildout version") - -parser.add_option("-t", "--accept-buildout-test-releases", - dest='accept_buildout_test_releases', - action="store_true", default=False, - help=("Normally, if you do not specify a --version, the " - "bootstrap script and buildout gets the newest " - "*final* versions of zc.buildout and its recipes and " - "extensions for you. If you use this flag, " - "bootstrap and buildout will get the newest releases " - "even if they are alphas or betas.")) -parser.add_option("-c", "--config-file", - help=("Specify the path to the buildout configuration " - "file to be used.")) -parser.add_option("-f", "--find-links", - help=("Specify a URL to search for buildout releases")) - - -options, args = parser.parse_args() - -###################################################################### -# load/install setuptools - -to_reload = False -try: - import pkg_resources - import setuptools -except ImportError: - ez = {} - - try: - from urllib.request import urlopen - except ImportError: - from urllib2 import urlopen - - # XXX use a more permanent ez_setup.py URL when available. - exec(urlopen('https://bitbucket.org/pypa/setuptools/raw/bootstrap/ez_setup.py' - ).read(), ez) - setup_args = dict(to_dir=tmpeggs, download_delay=0) - ez['use_setuptools'](**setup_args) - - if to_reload: - reload(pkg_resources) - import pkg_resources - # This does not (always?) update the default working set. We will - # do it. - for path in sys.path: - if path not in pkg_resources.working_set.entries: - pkg_resources.working_set.add_entry(path) - -###################################################################### -# Install buildout - -ws = pkg_resources.working_set - -cmd = [sys.executable, '-c', - 'from setuptools.command.easy_install import main; main()', - '-mZqNxd', tmpeggs] - -find_links = os.environ.get( - 'bootstrap-testing-find-links', - options.find_links or - ('http://downloads.buildout.org/' - if options.accept_buildout_test_releases else None) - ) -if find_links: - cmd.extend(['-f', find_links]) - -setuptools_path = ws.find( - pkg_resources.Requirement.parse('setuptools')).location - -requirement = 'zc.buildout' -version = options.version -if version is None and not options.accept_buildout_test_releases: - # Figure out the most recent final version of zc.buildout. - import setuptools.package_index - _final_parts = '*final-', '*final' - - def _final_version(parsed_version): - for part in parsed_version: - if (part[:1] == '*') and (part not in _final_parts): - return False - return True - index = setuptools.package_index.PackageIndex( - search_path=[setuptools_path]) - if find_links: - index.add_find_links((find_links,)) - req = pkg_resources.Requirement.parse(requirement) - if index.obtain(req) is not None: - best = [] - bestv = None - for dist in index[req.project_name]: - distv = dist.parsed_version - if _final_version(distv): - if bestv is None or distv > bestv: - best = [dist] - bestv = distv - elif distv == bestv: - best.append(dist) - if best: - best.sort() - version = best[-1].version -if version: - requirement = '=='.join((requirement, version)) -cmd.append(requirement) - -import subprocess -if subprocess.call(cmd, env=dict(os.environ, PYTHONPATH=setuptools_path)) != 0: - raise Exception( - "Failed to execute command:\n%s", - repr(cmd)[1:-1]) - -###################################################################### -# Import and run buildout - -ws.add_entry(tmpeggs) -ws.require(requirement) -import zc.buildout.buildout - -if not [a for a in args if '=' not in a]: - args.append('bootstrap') - -# if -c was provided, we push it back into args for buildout' main function -if options.config_file is not None: - args[0:0] = ['-c', options.config_file] - -zc.buildout.buildout.main(args) -shutil.rmtree(tmpeggs) diff --git a/buildout.cfg b/buildout.cfg deleted file mode 100644 index 8e26a69..0000000 --- a/buildout.cfg +++ /dev/null @@ -1,32 +0,0 @@ -[buildout] -parts = test coverage py -develop = . - -extensions = mr.developer -sources-dir = ${buildout:directory}/devsrc -always-checkout = force -auto-checkout = * - -[remotes] -cs = git://github.com/conestack -cs_push = git@github.com:conestack - -[sources] -yafowil = git ${remotes:cs}/yafowil.git pushurl=${remotes:cs_push}/yafowil.git - -[test] -recipe = zc.recipe.testrunner -eggs = - yafowil[test] - yafowil.widget.multiselect[test] -defaults = ['--auto-color', '--auto-progress'] - -[coverage] -recipe = zc.recipe.testrunner -eggs = ${test:eggs} -defaults = ['--coverage', '../../coverage', '-v', '--auto-progress'] - -[py] -recipe = zc.recipe.egg -eggs = ${test:eggs} -interpreter = py diff --git a/js/karma-coverage.js b/js/karma-coverage.js new file mode 100644 index 0000000..0072d7d --- /dev/null +++ b/js/karma-coverage.js @@ -0,0 +1,17 @@ +const percentage = { + lines: 100, + statements: 100, + functions: 100, + branches: 100 +} +var summary = require('./karma/coverage/coverage-summary.json'); + +for (let res in summary.total) { + if (summary.total[res].pct < percentage[res]) { + throw new Error( + `Coverage too low on ${res}, + expected: ${percentage[res]}, + got: ${summary.total[res].pct}` + ); + } +} \ No newline at end of file diff --git a/js/karma.conf.js b/js/karma.conf.js new file mode 100644 index 0000000..a12e076 --- /dev/null +++ b/js/karma.conf.js @@ -0,0 +1,70 @@ +// chromium binary +process.env.CHROME_BIN = '/usr/bin/chromium'; + +const RESOURCE_DIR = '../../src/yafowil/widget/multiselect/resources'; + +// karma config +module.exports = function(config) { + config.set({ + basePath: 'karma', + frameworks: [ + 'qunit' + ], + files: [{ + pattern: '../../node_modules/jquery/src/**/*.js', + type: 'module', + included: false + }, { + pattern: '../src/*.js', + type: 'module', + included: false + }, { + pattern: '../tests/test_*.js', + type: 'module' + }, { + pattern: `${RESOURCE_DIR}/multi-select/js/jquery.multi-select.js`, + type: 'module', + included: true + }], + browsers: [ + 'ChromeHeadless' + ], + autoWatch: false, + singleRun: true, + reporters: [ + 'progress', + 'coverage' + ], + coverageReporter: { + reporters: [ + { + type: 'json-summary', + dir: 'coverage/', + subdir: '.' + }, { + type: 'html', + dir: 'coverage/', + subdir: 'chrome-headless' + } + ] + }, + preprocessors: { + '../src/*.js': [ + 'coverage', + 'module-resolver' + ], + '../tests/*.js': [ + 'coverage', + 'module-resolver' + ] + }, + moduleResolverPreprocessor: { + addExtension: 'js', + customResolver: null, + ecmaVersion: 6, + aliases: { + jquery: '../../node_modules/jquery/src/jquery.js' + } + } + }); +}; \ No newline at end of file diff --git a/js/rollup.conf.js b/js/rollup.conf.js new file mode 100644 index 0000000..3e3c04a --- /dev/null +++ b/js/rollup.conf.js @@ -0,0 +1,47 @@ +import cleanup from 'rollup-plugin-cleanup'; +import {terser} from 'rollup-plugin-terser'; + +const out_dir = 'src/yafowil/widget/multiselect/resources'; + +const outro = ` +window.yafowil = window.yafowil || {}; +window.yafowil.multiselect = exports; +`; + +export default args => { + let conf = { + input: 'js/src/bundle.js', + plugins: [ + cleanup() + ], + output: [{ + name: 'yafowil_multiselect', + file: `${out_dir}/widget.js`, + format: 'iife', + outro: outro, + globals: { + jquery: 'jQuery' + }, + interop: 'default' + }], + external: [ + 'jquery' + ] + }; + if (args.configDebug !== true) { + conf.output.push({ + name: 'yafowil_multiselect', + file: `${out_dir}/widget.min.js`, + format: 'iife', + plugins: [ + terser() + ], + outro: outro, + globals: { + jquery: 'jQuery' + }, + interop: 'default' + }); + } + return conf; +}; diff --git a/js/src/bundle.js b/js/src/bundle.js new file mode 100644 index 0000000..ff3ab2a --- /dev/null +++ b/js/src/bundle.js @@ -0,0 +1,17 @@ +import $ from 'jquery'; + +import {MultiselectWidget} from './widget.js'; +import {register_array_subscribers} from './widget.js'; + +export * from './widget.js'; + +$(function() { + if (window.ts !== undefined) { + ts.ajax.register(MultiselectWidget.initialize, true); + } else if (window.bdajax !== undefined) { + bdajax.register(MultiselectWidget.initialize, true); + } else { + MultiselectWidget.initialize(); + } + register_array_subscribers(); +}); diff --git a/js/src/widget.js b/js/src/widget.js new file mode 100644 index 0000000..b4cd361 --- /dev/null +++ b/js/src/widget.js @@ -0,0 +1,35 @@ +import $ from 'jquery'; + +export class MultiselectWidget { + static initialize(context) { + $('select.multiselect', context).each(function() { + if (window.yafowil_array !== undefined && + window.yafowil_array.inside_template($(this))) { + return; + } + new MultiselectWidget($(this)); + }); + } + + constructor(elem) { + elem.data('yafowil-multiselect', this); + this.elem = elem; + this.elem.multiSelect(); + } +} + + +////////////////////////////////////////////////////////////////////////////// +// yafowil.widget.array integration +////////////////////////////////////////////////////////////////////////////// + +export function multiselect_on_array_add(inst, context) { + MultiselectWidget.initialize(context); +} + +export function register_array_subscribers() { + if (window.yafowil_array === undefined) { + return; + } + window.yafowil_array.on_array_event('on_add', multiselect_on_array_add); +} diff --git a/js/tests/test_widget.js b/js/tests/test_widget.js new file mode 100644 index 0000000..f8ab667 --- /dev/null +++ b/js/tests/test_widget.js @@ -0,0 +1,55 @@ +import {MultiselectWidget} from '../src/widget.js'; +import {register_array_subscribers} from "../src/widget"; + +QUnit.test('initialize', assert => { + let el = $('`).addClass('multiselect'); + $('td', table).addClass('arraytemplate'); + el.appendTo($('td', table)); + + // invoke array on_add - returns + _array_subscribers['on_add'].apply(null, $('tr', table)); + let widget = el.data('yafowil-multiselect'); + assert.notOk(widget); + $('td', table).removeClass('arraytemplate'); + + // invoke array on_add + el.attr('id', ''); + _array_subscribers['on_add'].apply(null, $('tr', table)); + widget = el.data('yafowil-multiselect'); + assert.ok(widget); + table.remove(); + window.yafowil_array = undefined; + _array_subscribers = undefined; +}); diff --git a/package.json b/package.json new file mode 100644 index 0000000..48e7441 --- /dev/null +++ b/package.json @@ -0,0 +1,17 @@ +{ + "scripts": { + "karma-test": "node_modules/karma/bin/karma start js/karma.conf.js", + "karma-test:coverage": "node js/karma-coverage.js" + }, + "devDependencies": { + "karma": "^6.4.1", + "karma-chrome-launcher": "^3.1.1", + "karma-coverage": "^2.2.0", + "karma-module-resolver-preprocessor": "^1.1.3", + "karma-qunit": "^4.1.2", + "qunit": "^2.19.3", + "rollup": "^2.79.1", + "rollup-plugin-cleanup": "^3.2.1", + "rollup-plugin-terser": "^7.0.2" + } +} diff --git a/scripts/clean.sh b/scripts/clean.sh new file mode 100755 index 0000000..a38829c --- /dev/null +++ b/scripts/clean.sh @@ -0,0 +1,23 @@ +#!/bin/bash +# +# Clean development environment. + +set -e + +to_remove=( + .coverage + dist + htmlcov + js/karma + node_modules + package-lock.json + py2 + py3 + pypy3 +) + +for item in "${to_remove[@]}"; do + if [ -e "$item" ]; then + rm -r "$item" + fi +done diff --git a/scripts/coverage.sh b/scripts/coverage.sh new file mode 100755 index 0000000..db810d0 --- /dev/null +++ b/scripts/coverage.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +set -e + +function run_coverage { + local target=$1 + + if [ -e "$target" ]; then + ./$target/bin/coverage run \ + --source src/yafowil/widget/multiselect \ + --omit src/yafowil/widget/multiselect/example.py \ + -m yafowil.widget.multiselect.tests + ./$target/bin/coverage report + ./$target/bin/coverage html + else + echo "Target $target not found." + fi +} + +run_coverage py2 +run_coverage py3 +run_coverage pypy3 diff --git a/scripts/install.sh b/scripts/install.sh new file mode 100755 index 0000000..55f3c7d --- /dev/null +++ b/scripts/install.sh @@ -0,0 +1,43 @@ +#!/bin/bash +# +# Install development environment. + +set -e + +./scripts/clean.sh + +if ! which npm &> /dev/null; then + sudo apt-get install npm +fi + +npm --prefix . --save-dev install \ + qunit \ + karma \ + karma-qunit \ + karma-coverage \ + karma-chrome-launcher \ + karma-module-resolver-preprocessor \ + rollup \ + rollup-plugin-cleanup \ + rollup-plugin-terser + +npm --prefix . --no-save install https://github.com/jquery/jquery#main + +function install { + local interpreter=$1 + local target=$2 + + if [ -x "$(which $interpreter)" ]; then + virtualenv --clear -p $interpreter $target + ./$target/bin/pip install wheel coverage + ./$target/bin/pip install https://github.com/conestack/webresource/archive/master.zip + ./$target/bin/pip install https://github.com/conestack/yafowil/archive/master.zip + ./$target/bin/pip install -e .[test] + else + echo "Interpreter $interpreter not found. Skip install." + fi +} + +install python2 py2 +install python3 py3 +install pypy3 pypy3 diff --git a/scripts/karma.sh b/scripts/karma.sh new file mode 100755 index 0000000..b04cf41 --- /dev/null +++ b/scripts/karma.sh @@ -0,0 +1,6 @@ +#!/bin/bash +# +# Run karma tests. + +clear +node_modules/karma/bin/karma start js/karma.conf.js diff --git a/scripts/rollup.sh b/scripts/rollup.sh new file mode 100755 index 0000000..ba06613 --- /dev/null +++ b/scripts/rollup.sh @@ -0,0 +1,5 @@ +#!/bin/bash +# +# Create JS bundles. + +node_modules/rollup/dist/bin/rollup --config js/rollup.conf.js "$@" diff --git a/scripts/test.sh b/scripts/test.sh new file mode 100755 index 0000000..4caafbd --- /dev/null +++ b/scripts/test.sh @@ -0,0 +1,17 @@ +#!/bin/bash +# +# Run python tests. + +function run_tests { + local target=$1 + + if [ -e "$target" ]; then + ./$target/bin/python -m yafowil.widget.multiselect.tests + else + echo "Target $target not found." + fi +} + +run_tests py2 +run_tests py3 +run_tests pypy3 diff --git a/scripts/watch.sh b/scripts/watch.sh new file mode 100755 index 0000000..1973006 --- /dev/null +++ b/scripts/watch.sh @@ -0,0 +1,11 @@ +#!/bin/bash +# +# Watch source directory for changes and run rollup.sh. +# +# Install dependencies: +# sudo apt-install inotify-tools + +while res=$(inotifywait -e create -e modify -e delete -e move ./js/src); do + echo "changed: $res, run rollup" + ./scripts/rollup.sh --configDebug +done diff --git a/setup.py b/setup.py index 70988fa..2e324a2 100644 --- a/setup.py +++ b/setup.py @@ -8,14 +8,13 @@ def read_file(name): return f.read() -version = '1.4.dev0' +version = '2.0.dev0' shortdesc = 'multiselect widget for YAFOWIL' longdesc = '\n\n'.join([read_file(name) for name in [ 'README.rst', 'CHANGES.rst', 'LICENSE.rst' ]]) -tests_require = ['yafowil[test]'] setup( @@ -48,10 +47,14 @@ def read_file(name): 'setuptools', 'yafowil>2.1.99', ], - tests_require=tests_require, - extras_require=dict( - test=tests_require, - ), + tests_require=[ + 'lxml', + 'zope.testrunner' + ], + extras_require=dict(test=[ + 'lxml', + 'zope.testrunner' + ]), test_suite="yafowil.widget.multiselect.tests", entry_points=""" [yafowil.plugin] diff --git a/src/yafowil/widget/multiselect/__init__.py b/src/yafowil/widget/multiselect/__init__.py index c09c41f..2813eb8 100644 --- a/src/yafowil/widget/multiselect/__init__.py +++ b/src/yafowil/widget/multiselect/__init__.py @@ -1,9 +1,50 @@ from yafowil.base import factory from yafowil.utils import entry_point import os +import webresource as wr -resourcedir = os.path.join(os.path.dirname(__file__), 'resources') +resources_dir = os.path.join(os.path.dirname(__file__), 'resources') + + +############################################################################## +# Default +############################################################################## + +# webresource ################################################################ + +resources = wr.ResourceGroup( + name='yafowil.widget.multiselect', + directory=resources_dir, + path='yafowil-multiselect' +) +resources.add(wr.ScriptResource( + name='multiselect-js', + depends='jquery-js', + directory=os.path.join(resources_dir, 'multi-select', 'js'), + path='yafowil-multiselect/multi-select/js', + resource='jquery.multi-select.js' +)) +resources.add(wr.ScriptResource( + name='yafowil-multiselect-js', + depends='multiselect-js', + resource='widget.js', + compressed='widget.min.js' +)) +resources.add(wr.StyleResource( + name='multiselect-css', + directory=os.path.join(resources_dir, 'multi-select', 'css'), + path='yafowil-multiselect/multi-select/css', + resource='multi-select.css' +)) +resources.add(wr.StyleResource( + name='yafowil-multiselect-css', + depends='multiselect-css', + resource='widget.css' +)) + +# B/C resources ############################################################## + js = [{ 'group': 'yafowil.widget.multiselect.dependencies', 'resource': 'multi-select/js/jquery.multi-select.js', @@ -24,8 +65,22 @@ }] +############################################################################## +# Registration +############################################################################## + @entry_point(order=10) def register(): from yafowil.widget.multiselect import widget # noqa - factory.register_theme('default', 'yafowil.widget.multiselect', - resourcedir, js=js, css=css) + + widget_name = 'yafowil.widget.multiselect' + + # Default + factory.register_theme( + 'default', + widget_name, + resources_dir, + js=js, + css=css + ) + factory.register_resources('default', widget_name, resources) diff --git a/src/yafowil/widget/multiselect/resources/widget.css b/src/yafowil/widget/multiselect/resources/widget.css index 25dcb6c..e947b46 100644 --- a/src/yafowil/widget/multiselect/resources/widget.css +++ b/src/yafowil/widget/multiselect/resources/widget.css @@ -2,3 +2,8 @@ iframe.multiselect-sandbox { width:100% !important; height:30em !important; } + +.ms-container .ms-selectable li.ms-hover, .ms-container .ms-selection li.ms-hover { + background-color: var(--yafowil-accent-color, #0d6efd); + color: var(--yafowil-accent-font-color, #fff); +} diff --git a/src/yafowil/widget/multiselect/resources/widget.js b/src/yafowil/widget/multiselect/resources/widget.js index 82b7659..ba23292 100644 --- a/src/yafowil/widget/multiselect/resources/widget.js +++ b/src/yafowil/widget/multiselect/resources/widget.js @@ -1,42 +1,54 @@ -/* - * yafowil multiselect widget - * - * Optional: bdajax - */ - -if (typeof(window['yafowil']) == "undefined") yafowil = {}; - -(function($) { - - $(document).ready(function() { - // initial binding - yafowil.multiselect.binder(); - - // add after ajax binding if bdajax present - if (typeof(window['bdajax']) != "undefined") { - $.extend(bdajax.binders, { - multiselect_binder: yafowil.multiselect.binder +var yafowil_multiselect = (function (exports, $) { + 'use strict'; + + class MultiselectWidget { + static initialize(context) { + $('select.multiselect', context).each(function() { + if (window.yafowil_array !== undefined && + window.yafowil_array.inside_template($(this))) { + return; + } + new MultiselectWidget($(this)); }); } + constructor(elem) { + elem.data('yafowil-multiselect', this); + this.elem = elem; + this.elem.multiSelect(); + } + } + function multiselect_on_array_add(inst, context) { + MultiselectWidget.initialize(context); + } + function register_array_subscribers() { + if (window.yafowil_array === undefined) { + return; + } + window.yafowil_array.on_array_event('on_add', multiselect_on_array_add); + } + + $(function() { + if (window.ts !== undefined) { + ts.ajax.register(MultiselectWidget.initialize, true); + } else if (window.bdajax !== undefined) { + bdajax.register(MultiselectWidget.initialize, true); + } else { + MultiselectWidget.initialize(); + } + register_array_subscribers(); }); - $.extend(yafowil, { - - multiselect: { - - binder: function(context) { + exports.MultiselectWidget = MultiselectWidget; + exports.multiselect_on_array_add = multiselect_on_array_add; + exports.register_array_subscribers = register_array_subscribers; - $('select.multiselect', context).each(function(event) { + Object.defineProperty(exports, '__esModule', { value: true }); - var id = $(this).attr('id'); - var element = $('#' + id); - element.multiSelect(); - }); + window.yafowil = window.yafowil || {}; + window.yafowil.multiselect = exports; - } - } - }); + return exports; -})(jQuery); +})({}, jQuery); diff --git a/src/yafowil/widget/multiselect/resources/widget.min.js b/src/yafowil/widget/multiselect/resources/widget.min.js new file mode 100644 index 0000000..e676973 --- /dev/null +++ b/src/yafowil/widget/multiselect/resources/widget.min.js @@ -0,0 +1 @@ +var yafowil_multiselect=function(i,e){"use strict";class t{static initialize(i){e("select.multiselect",i).each((function(){void 0!==window.yafowil_array&&window.yafowil_array.inside_template(e(this))||new t(e(this))}))}constructor(i){i.data("yafowil-multiselect",this),this.elem=i,this.elem.multiSelect()}}function a(i,e){t.initialize(e)}function l(){void 0!==window.yafowil_array&&window.yafowil_array.on_array_event("on_add",a)}return e((function(){void 0!==window.ts?ts.ajax.register(t.initialize,!0):void 0!==window.bdajax?bdajax.register(t.initialize,!0):t.initialize(),l()})),i.MultiselectWidget=t,i.multiselect_on_array_add=a,i.register_array_subscribers=l,Object.defineProperty(i,"__esModule",{value:!0}),window.yafowil=window.yafowil||{},window.yafowil.multiselect=i,i}({},jQuery); diff --git a/src/yafowil/widget/multiselect/tests.py b/src/yafowil/widget/multiselect/tests.py index 9bfd719..99cfd29 100644 --- a/src/yafowil/widget/multiselect/tests.py +++ b/src/yafowil/widget/multiselect/tests.py @@ -2,20 +2,26 @@ from yafowil.base import factory from yafowil.compat import IS_PY2 from yafowil.tests import YafowilTestCase +import os import unittest -import yafowil.loader # noqa if not IS_PY2: from importlib import reload +def np(path): + return path.replace('/', os.path.sep) + + class TestMultiselectWidget(YafowilTestCase): def setUp(self): super(TestMultiselectWidget, self).setUp() + from yafowil.widget import multiselect from yafowil.widget.multiselect import widget reload(widget) + multiselect.register() def test_edit_renderer(self): # Render widget @@ -73,6 +79,49 @@ def test_extraction(self): self.assertEqual(data.errors, []) self.assertEqual(data.extracted, ['1']) + def test_resources(self): + factory.theme = 'default' + resources = factory.get_resources('yafowil.widget.multiselect') + self.assertTrue( + resources.directory.endswith(np('/multiselect/resources')) + ) + self.assertEqual(resources.name, 'yafowil.widget.multiselect') + self.assertEqual(resources.path, 'yafowil-multiselect') + + scripts = resources.scripts + self.assertEqual(len(scripts), 2) + + self.assertTrue(scripts[0].directory.endswith( + np('/multiselect/resources/multi-select/js') + )) + self.assertEqual(scripts[0].path, 'yafowil-multiselect/multi-select/js') + self.assertEqual(scripts[0].file_name, 'jquery.multi-select.js') + self.assertTrue(os.path.exists(scripts[0].file_path)) + + self.assertTrue( + scripts[1].directory.endswith(np('/multiselect/resources')) + ) + self.assertEqual(scripts[1].path, 'yafowil-multiselect') + self.assertEqual(scripts[1].file_name, 'widget.min.js') + self.assertTrue(os.path.exists(scripts[1].file_path)) + + styles = resources.styles + self.assertEqual(len(styles), 2) + + self.assertTrue(styles[0].directory.endswith( + np('/multiselect/resources/multi-select/css') + )) + self.assertEqual(styles[0].path, 'yafowil-multiselect/multi-select/css') + self.assertEqual(styles[0].file_name, 'multi-select.css') + self.assertTrue(os.path.exists(styles[0].file_path)) + + self.assertTrue( + styles[1].directory.endswith(np('/multiselect/resources')) + ) + self.assertEqual(styles[1].path, 'yafowil-multiselect') + self.assertEqual(styles[1].file_name, 'widget.css') + self.assertTrue(os.path.exists(styles[1].file_path)) + if __name__ == '__main__': unittest.main()