├── .node-version ├── .npmignore ├── .browserslistrc ├── .eslintignore ├── demo ├── img │ ├── 1.png │ ├── 2.png │ └── 3.png ├── data │ ├── 1.json │ ├── 2.json │ └── 3.json └── index.html ├── docs └── img │ └── preview.png ├── .gitignore ├── .stylelintrc.json ├── .eslintrc.js ├── LICENSE ├── .github └── workflows │ ├── ci.yml │ └── release.yml ├── src ├── .eslintrc.js ├── @types │ └── jq.schedule.d.ts ├── sass │ └── style.scss └── js │ └── jq.schedule.js ├── package.json ├── dist ├── css │ ├── style.min.css │ └── style.css └── js │ ├── jq.schedule.min.js │ ├── jq.schedule.js │ └── jq.schedule.min.js.map ├── gulpfile.js └── README.md /.node-version: -------------------------------------------------------------------------------- 1 | v20.18.0 2 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .idea 2 | logs/ 3 | tmp/ 4 | node_modules/ -------------------------------------------------------------------------------- /.browserslistrc: -------------------------------------------------------------------------------- 1 | > 0.25% 2 | last 2 version 3 | Firefox ESR 4 | not dead -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | .eslintrc.js 2 | .css/ 3 | demo/ 4 | node_modules 5 | dist -------------------------------------------------------------------------------- /demo/img/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ateliee/jquery.schedule/HEAD/demo/img/1.png -------------------------------------------------------------------------------- /demo/img/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ateliee/jquery.schedule/HEAD/demo/img/2.png -------------------------------------------------------------------------------- /demo/img/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ateliee/jquery.schedule/HEAD/demo/img/3.png -------------------------------------------------------------------------------- /docs/img/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ateliee/jquery.schedule/HEAD/docs/img/preview.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | .idea 3 | node_modules 4 | logs 5 | tmp 6 | !.gitkeep -------------------------------------------------------------------------------- /.stylelintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "stylelint-config-standard-scss", 3 | "rules": { 4 | "selector-class-pattern": null, 5 | "no-descending-specificity": null, 6 | "scss/no-global-function-names": null 7 | } 8 | } -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "extends": [ 3 | "eslint:recommended", 4 | "plugin:compat/recommended" 5 | ], 6 | "plugins": [ 7 | "compat" 8 | ], 9 | "parserOptions": { 10 | "ecmaVersion": 2022, 11 | }, 12 | "env": { 13 | "node": true, 14 | "browser": true 15 | }, 16 | "globals": { 17 | "$": "readonly", 18 | "jQuery": "readonly" 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /demo/data/1.json: -------------------------------------------------------------------------------- 1 | { 2 | "0" : { 3 | "title" : "Example1", 4 | "schedule":[ 5 | { 6 | "start": "11:00", 7 | "end": "12:00", 8 | "text": "Ajax Text Area", 9 | "data": { 10 | } 11 | }, 12 | { 13 | "start": "10:30", 14 | "end": "15:00", 15 | "text": "Ajax Text Area2", 16 | "data": { 17 | } 18 | } 19 | ] 20 | }, 21 | "1" : { 22 | "title" : "Example1", 23 | "schedule":[ 24 | { 25 | "start": "15:00", 26 | "end": "18:00", 27 | "text": "Text Area", 28 | "data": { 29 | } 30 | } 31 | ] 32 | } 33 | } -------------------------------------------------------------------------------- /demo/data/2.json: -------------------------------------------------------------------------------- 1 | { 2 | "0" : { 3 | "title" : "Example1", 4 | "schedule":[ 5 | { 6 | "start": "08:00", 7 | "end": "12:00", 8 | "text": "TestTestTestTestTestTest", 9 | "data": { 10 | "class": "example3" 11 | } 12 | } 13 | ] 14 | }, 15 | "1" : { 16 | "title" : "Example2", 17 | "schedule":[ 18 | { 19 | "start": "15:00", 20 | "end": "18:00", 21 | "text": "Text Area", 22 | "data": { 23 | } 24 | } 25 | ] 26 | }, 27 | "2" : { 28 | "title" : "Example3", 29 | "schedule":[ 30 | { 31 | "start": "08:30", 32 | "end": "14:00", 33 | "text": "Text Area", 34 | "data": { 35 | } 36 | } 37 | ] 38 | }, 39 | "3" : { 40 | "title" : "Example4", 41 | "schedule":[ 42 | { 43 | "start": "11:00", 44 | "end": "14:00", 45 | "text": "Text Area", 46 | "data": { 47 | } 48 | } 49 | ] 50 | } 51 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 ateliee 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ master, main ] 6 | pull_request: 7 | branches: [ master, main ] 8 | 9 | jobs: 10 | test: 11 | runs-on: ubuntu-latest 12 | strategy: 13 | matrix: 14 | node-version: [20, 22, 24] 15 | 16 | steps: 17 | - uses: actions/checkout@v4 18 | 19 | - name: Use Node.js ${{ matrix.node-version }} 20 | uses: actions/setup-node@v4 21 | with: 22 | node-version: ${{ matrix.node-version }} 23 | cache: 'npm' 24 | 25 | - name: Install dependencies 26 | run: npm ci 27 | 28 | - name: Run tests 29 | run: | 30 | echo "Running tests against $(node -v) ..." 31 | npm test 32 | 33 | - name: Build 34 | run: npm run build 35 | 36 | lint: 37 | runs-on: ubuntu-latest 38 | 39 | steps: 40 | - uses: actions/checkout@v4 41 | 42 | - name: Use Node.js 43 | uses: actions/setup-node@v4 44 | with: 45 | node-version: '22' 46 | cache: 'npm' 47 | 48 | - name: Install dependencies 49 | run: npm ci 50 | 51 | - name: Run lint 52 | run: | 53 | echo "lint start" 54 | npm run lint 55 | -------------------------------------------------------------------------------- /src/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "env": { 3 | "browser": true, 4 | "node": false, 5 | 'jquery': true 6 | }, 7 | 'extends': [ 8 | 'eslint:recommended' 9 | ], 10 | "globals": { 11 | "Atomics": "readonly", 12 | "SharedArrayBuffer": "readonly", 13 | "console": true, 14 | "$": true 15 | }, 16 | "plugins": [ 17 | "no-jquery" 18 | ], 19 | "rules": { 20 | "indent": [2, 4], 21 | "vars-on-top": "off", 22 | "no-restricted-syntax": "off", 23 | "guard-for-in": "off", 24 | "no-useless-concat": "off", 25 | "operator-linebreak": "off", 26 | "no-plusplus": "off", 27 | "block-scoped-var": "off", 28 | "func-names": "off", 29 | "no-continue": "off", 30 | "no-bitwise": "warn", 31 | "no-mixed-operators": "warn", 32 | "one-var": "off", 33 | "one-var-declaration-per-line": "off", 34 | "max-len": ["error", {code: 180}], 35 | "no-param-reassign": "warn", 36 | "no-else-return": "warn", 37 | "no-underscore-dangle": "off", 38 | "no-unneeded-ternary": "off", 39 | "no-jquery/variable-pattern": "error" 40 | } 41 | }; 42 | -------------------------------------------------------------------------------- /demo/data/3.json: -------------------------------------------------------------------------------- 1 | { 2 | "0" : { 3 | "title" : "Taro", 4 | "subtitle" : "Developer", 5 | "schedule":[ 6 | { 7 | "start": "08:00", 8 | "end": "10:00", 9 | "text": "Meeting", 10 | "data": { 11 | "class": "example2", 12 | "image": "./img/1.png" 13 | } 14 | }, 15 | { 16 | "start": "10:30", 17 | "end": "11:20", 18 | "text": "Interview(Aika)", 19 | "data": { 20 | "class": "example3", 21 | "image": "./img/2.png" 22 | } 23 | }, 24 | { 25 | "start": "13:00", 26 | "end": "18:00", 27 | "text": "Go out", 28 | "data": { 29 | } 30 | } 31 | ] 32 | }, 33 | "1" : { 34 | "title" : "Saito", 35 | "subtitle" : "Management", 36 | "schedule":[ 37 | { 38 | "start": "08:00", 39 | "end": "10:00", 40 | "text": "Meeting", 41 | "data": { 42 | "class": "example2", 43 | "image": "./img/1.png" 44 | } 45 | } 46 | ] 47 | }, 48 | "2" : { 49 | "title" : "Daisuke Kato", 50 | "subtitle" : "Management", 51 | "schedule":[ 52 | { 53 | "start": "11:00", 54 | "end": "14:00", 55 | "text": "Go out", 56 | "data": { 57 | "image": "./img/3.png" 58 | } 59 | } 60 | ] 61 | } 62 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@ateliee/jq.schedule", 3 | "version": "4.0.3", 4 | "description": "jQuery time schedule plugin", 5 | "main": "src/js/jq.schedule.js", 6 | "types": "src/@types/jq.schedule.d.ts", 7 | "scripts": { 8 | "serve": "gulp serve", 9 | "test": "npm run lint && npm run stylelint", 10 | "lint": "npx eslint .", 11 | "lint:fix": "npx eslint . --fix", 12 | "stylelint": "npx stylelint \"src/**/*.scss\"", 13 | "stylelint:fix": "npm run stylelint -- --fix", 14 | "build": "gulp build", 15 | "git:tag": "gulp tag" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "git+https://github.com/ateliee/jquery.schedule.git" 20 | }, 21 | "keywords": [ 22 | "jquery", 23 | "front-end", 24 | "schedule", 25 | "calendar", 26 | "web", 27 | "ajax", 28 | "responsible" 29 | ], 30 | "author": "ateliee (https://ateliee.com)", 31 | "license": "MIT", 32 | "bugs": { 33 | "url": "https://github.com/ateliee/jquery.schedule/issues" 34 | }, 35 | "homepage": "https://github.com/ateliee/jquery.schedule#readme", 36 | "dependencies": { 37 | "jquery": "^3.7.1", 38 | "jquery-ui-dist": "^1.13.3" 39 | }, 40 | "engines": { 41 | "node": ">=20.0.0", 42 | "npm": ">=10.0.0" 43 | }, 44 | "devDependencies": { 45 | "@babel/core": "^7.27.4", 46 | "@babel/preset-env": "^7.27.2", 47 | "browser-sync": "^3.0.4", 48 | "del": "^8.0.0", 49 | "eslint": "^8.57.0", 50 | "eslint-plugin-compat": "^6.0.2", 51 | "eslint-plugin-import": "^2.31.0", 52 | "eslint-plugin-no-jquery": "^3.1.1", 53 | "gulp": "^5.0.1", 54 | "gulp-babel": "^8.0.0", 55 | "gulp-plumber": "^1.2.1", 56 | "gulp-rename": "^2.0.0", 57 | "gulp-sass": "^6.0.1", 58 | "gulp-sourcemaps": "^2.6.5", 59 | "gulp-tag-version": "^1.3.1", 60 | "gulp-uglify": "^3.0.2", 61 | "husky": "^8.0.3", 62 | "minimist": "^1.2.8", 63 | "readable-stream": "^4.7.0", 64 | "sass": "^1.89.2", 65 | "stylelint": "^16.21.0", 66 | "stylelint-config-standard-scss": "^15.0.1" 67 | }, 68 | "husky": { 69 | "hooks": { 70 | "pre-commit": "npm test && npm run build" 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/@types/jq.schedule.d.ts: -------------------------------------------------------------------------------- 1 | 2 | interface ScheduleData { 3 | start: string 4 | end: string 5 | startTime: number 6 | endTime: number 7 | text: string 8 | timeline?: number 9 | data: {[s: string]: any} 10 | } 11 | interface RowData { 12 | title: string 13 | schedule: ScheduleData[] 14 | } 15 | 16 | /** 17 | * オプション設定 18 | */ 19 | interface Options { 20 | className?: string 21 | rows: {[s: string]: RowData} 22 | startTime?: string 23 | endTime?: string 24 | widthTimeX?: number 25 | widthTime?: number 26 | timeLineY?: number 27 | timeLineBorder?: number 28 | timeBorder?: number 29 | timeLinePaddingTop?: number 30 | timeLinePaddingBotto?: number 31 | headTimeBorder?: number 32 | dataWidth?: number 33 | verticalScrollbar?: number 34 | bundleMoveWidth?: number 35 | draggable?: boolean 36 | resizable?: boolean 37 | resizableLeft?: boolean 38 | onInitRow?(node: jQuery, data: RowData): void 39 | onChange?(timeline: number, data: ScheduleData): void 40 | onClick?(node: jQuery, data: ScheduleData): void 41 | onAppendRow?(node: jQuery, data: ScheduleData): void 42 | onAppendSchedule?(node: jQuery, data: ScheduleData): void 43 | onScheduleClick?(node: jQuery, time: string, data: ScheduleData): void 44 | } 45 | interface SaveData { 46 | tableStartTime: number 47 | tableEndTime: number 48 | schedule: ScheduleData[] 49 | timeline: Object[] 50 | } 51 | interface jQuery { 52 | timeSchedule(options: Options): jQuery; 53 | timeSchedule(method: "timelineData"): RowData[]; 54 | timeSchedule(method: "scheduleData"): RowData[]; 55 | timeSchedule(method: "resetData"): jQuery; 56 | timeSchedule(method: "resetRowData"): jQuery; 57 | timeSchedule(method: "setDraggable" | "setResizable", enable: boolean): jQuery; 58 | timeSchedule(method: "setRows", data: RowData): RowData; 59 | timeSchedule(method: "addSchedule", timeline: number, data: ScheduleData): jQuery; 60 | timeSchedule(method: "addRow", timeline: number, data: RowData): jQuery; 61 | timeSchedule(method: "formatTime", val: number): string; 62 | timeSchedule(method: "calcStringTime", str: string): number; 63 | } -------------------------------------------------------------------------------- /dist/css/style.min.css: -------------------------------------------------------------------------------- 1 | .jq-schedule *{box-sizing:border-box}.jq-schedule .sc_wrapper::after,.jq-schedule .sc_menu::after{content:"";display:table;clear:both}.jq-schedule .sc_menu{width:100%;height:26px}.jq-schedule .sc_menu .sc_header_cell{float:left}.jq-schedule .sc_menu .sc_header{float:left;height:26px;position:relative;overflow:hidden}.jq-schedule .sc_menu .sc_header .sc_time{text-align:center;border-left:solid 1px #fff;background:#555}.jq-schedule .sc_menu .sc_header_cell,.jq-schedule .sc_data{float:left;font-weight:bold;color:#fff;background:#555;position:relative}.jq-schedule .sc_menu .sc_header_scroll,.jq-schedule .sc_data .sc_data_scroll{position:absolute;left:0;top:0}.jq-schedule .sc_menu .sc_header_cell,.jq-schedule .sc_header .sc_time,.jq-schedule .sc_main_scroll .sc_time{color:#fff;padding:0;line-height:26px;height:26px;display:block}.jq-schedule .sc_header .sc_time,.jq-schedule .sc_main_scroll .sc_time{float:left}.jq-schedule .sc_main_box,.jq-schedule .sc_data{max-height:500px;overflow:hidden}.jq-schedule .sc_main_box{float:left;overflow:auto}.jq-schedule .sc_main{position:relative}.jq-schedule .timeline{position:relative}.jq-schedule .ui-draggable-dragging,.jq-schedule .ui-resizeable{z-index:20}.jq-schedule .sc_bar{position:absolute;color:#fff;background:#4f93d6;cursor:pointer;z-index:10}.jq-schedule .sc_bar .head{display:block;margin-top:6px;font-size:12px;padding:0 14px;height:1.2em;overflow:hidden}.jq-schedule .sc_bar .text{display:block;margin-top:6px;font-weight:bold;padding:0 14px;height:1.2em;overflow:hidden}.jq-schedule .sc_bar .ui-resizable-handle{display:block;content:" ";position:absolute;height:100%;right:0;top:0;width:5px;background:rgb(45.7235023041,121.5576036866,196.2764976959)}.jq-schedule .sc_bar .ui-resizable-handle.ui-resizable-e{right:0}.jq-schedule .sc_bar .ui-resizable-handle.ui-resizable-w{left:0}.jq-schedule .timeline,.jq-schedule .sc_main .tb{border-bottom:solid 2px #666}.jq-schedule .sc_data .timeline{overflow:hidden;padding:10px}.jq-schedule .sc_data .timeline span{display:block}.jq-schedule .sc_data .timeline span.timeline-subtitle{font-size:.8em;color:#ccc}.jq-schedule .sc_main_scroll .sc_main .tl{float:left;height:100%;border-right:solid 1px #ccc}.jq-schedule .sc_main_scroll .sc_main .tl:hover{background:#f0f0f0}.jq-schedule .ui-state-disabled{opacity:1}.jq-schedule .ui-state-disabled .ui-resizable-handle{display:none}.jq-schedule .ui-state-disabled .ui-resizable-handle:hover{cursor:auto}.jq-schedule .ui-draggable-disabled{opacity:.8} -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | let gulp = require('gulp'); 2 | let uglify = require('gulp-uglify'); 3 | let sourcemaps = require('gulp-sourcemaps'); 4 | let rename = require('gulp-rename'); 5 | let plumber = require('gulp-plumber'); 6 | const { deleteAsync } = require('del'); 7 | let sass = require('gulp-sass')(require('sass')); 8 | let browserSync = require('browser-sync'); 9 | let tagVersion = require('gulp-tag-version'); 10 | let babel = require('gulp-babel'); 11 | 12 | // js minify 13 | gulp.task('js-minify', function () { 14 | return gulp.src(['./dist/**/*.js', '!./dist/**/*.min.js']) 15 | .pipe(plumber()) 16 | .pipe(sourcemaps.init()) 17 | .pipe(uglify()) 18 | .pipe(rename({ suffix: '.min' })) 19 | .pipe(sourcemaps.write('.')) 20 | .pipe(gulp.dest('./dist/')); 21 | }); 22 | 23 | // dist clean 24 | gulp.task('clean-dist', function() { 25 | return deleteAsync(['dist/**/*']); 26 | }); 27 | // compile 28 | gulp.task('compile', function() { 29 | return gulp.src('src/**/*.js') 30 | .pipe(babel({ 31 | presets: ['@babel/preset-env'] 32 | })) 33 | .pipe(gulp.dest('dist')); 34 | }); 35 | // sass compile 36 | gulp.task('sass-minify', function () { 37 | return gulp.src('./src/sass/**/*.scss') 38 | .pipe(sass({style: 'compressed'}).on('error', sass.logError)) 39 | .pipe(rename({ suffix: '.min' })) 40 | .pipe(gulp.dest('./dist/css')); 41 | }); 42 | gulp.task('sass', function () { 43 | return gulp.src('./src/sass/**/*.scss') 44 | .pipe(sass().on('error', sass.logError)) 45 | .pipe(gulp.dest('./dist/css')); 46 | }); 47 | // build 48 | gulp.task('build', gulp.series( 49 | 'clean-dist', 50 | gulp.parallel( 51 | 'compile', 52 | 'sass' 53 | ), 54 | gulp.parallel( 55 | 'js-minify', 56 | 'sass-minify' 57 | ) 58 | )); 59 | gulp.task('tag', function() { 60 | return gulp.src(['./package.json']).pipe(tagVersion()); 61 | }); 62 | 63 | // Static server 64 | gulp.task('browser-sync', function() { 65 | return browserSync.init({ 66 | server: { 67 | baseDir: ".", 68 | }, 69 | open: false, 70 | // browser: 'google chrome', 71 | startPath: 'demo/index.html' 72 | }); 73 | }); 74 | gulp.task('bs-reload', function (done) { 75 | browserSync.reload(); 76 | done(); 77 | }); 78 | 79 | // Watch scss AND html files, doing different things with each. 80 | gulp.task('watch', function (done) { 81 | gulp.watch("./src/sass/*.scss", gulp.series( 82 | 'sass-minify', 83 | 'bs-reload' 84 | )); 85 | gulp.watch("./src/js/*.js", gulp.series( 86 | 'compile', 87 | 'js-minify', 88 | 'bs-reload' 89 | )); 90 | gulp.watch("./demo/*", gulp.series( 91 | 'bs-reload' 92 | )); 93 | done(); 94 | }); 95 | gulp.task('serve', gulp.series('build', 'watch', 'browser-sync')); 96 | gulp.task('default', gulp.task('serve')); 97 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | 7 | jobs: 8 | create-tag: 9 | runs-on: ubuntu-latest 10 | if: github.ref == 'refs/heads/master' 11 | outputs: 12 | version: ${{ steps.get-version.outputs.version }} 13 | tag-created: ${{ steps.create-tag.outputs.tag-created }} 14 | 15 | steps: 16 | - uses: actions/checkout@v4 17 | with: 18 | fetch-depth: 0 19 | 20 | - name: Get package version 21 | id: get-version 22 | run: | 23 | VERSION=$(node -p "require('./package.json').version") 24 | echo "version=v$VERSION" >> $GITHUB_OUTPUT 25 | echo "Package version: v$VERSION" 26 | 27 | - name: Check if tag exists 28 | id: check-tag 29 | run: | 30 | if git tag -l "${{ steps.get-version.outputs.version }}" | grep -q "${{ steps.get-version.outputs.version }}"; then 31 | echo "tag-exists=true" >> $GITHUB_OUTPUT 32 | echo "Tag ${{ steps.get-version.outputs.version }} already exists" 33 | else 34 | echo "tag-exists=false" >> $GITHUB_OUTPUT 35 | echo "Tag ${{ steps.get-version.outputs.version }} does not exist" 36 | fi 37 | 38 | - name: Create and push tag 39 | id: create-tag 40 | if: steps.check-tag.outputs.tag-exists == 'false' 41 | run: | 42 | git config user.name "github-actions[bot]" 43 | git config user.email "github-actions[bot]@users.noreply.github.com" 44 | git tag -a "${{ steps.get-version.outputs.version }}" -m "Release ${{ steps.get-version.outputs.version }}" 45 | git push origin "${{ steps.get-version.outputs.version }}" 46 | echo "tag-created=true" >> $GITHUB_OUTPUT 47 | echo "Created and pushed tag: ${{ steps.get-version.outputs.version }}" 48 | 49 | github-package-release: 50 | runs-on: ubuntu-latest 51 | needs: create-tag 52 | if: github.ref == 'refs/heads/master' 53 | 54 | steps: 55 | - uses: actions/checkout@v4 56 | 57 | - name: Use Node.js 58 | uses: actions/setup-node@v4 59 | with: 60 | node-version: '22' 61 | cache: 'npm' 62 | registry-url: 'https://npm.pkg.github.com' 63 | scope: '@ateliee' 64 | 65 | - name: Install dependencies 66 | run: npm ci 67 | 68 | - name: Build 69 | run: | 70 | echo "Deploying to github ..." 71 | npm run build 72 | 73 | - name: Publish to GitHub Package Registry 74 | run: npm publish 75 | env: 76 | NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 77 | 78 | npm-package-release: 79 | runs-on: ubuntu-latest 80 | needs: create-tag 81 | if: github.ref == 'refs/heads/master' 82 | 83 | steps: 84 | - uses: actions/checkout@v4 85 | 86 | - name: Use Node.js 87 | uses: actions/setup-node@v4 88 | with: 89 | node-version: '22' 90 | cache: 'npm' 91 | registry-url: 'https://registry.npmjs.org' 92 | 93 | - name: Install dependencies 94 | run: npm ci 95 | 96 | - name: Build 97 | run: | 98 | echo "Deploying to npm ..." 99 | npm run build 100 | 101 | - name: Publish to NPM Registry 102 | run: npm publish --access public 103 | env: 104 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 105 | -------------------------------------------------------------------------------- /dist/css/style.css: -------------------------------------------------------------------------------- 1 | .jq-schedule * { 2 | box-sizing: border-box; 3 | } 4 | .jq-schedule .sc_wrapper::after, 5 | .jq-schedule .sc_menu::after { 6 | content: ""; 7 | display: table; 8 | clear: both; 9 | } 10 | .jq-schedule .sc_menu { 11 | width: 100%; 12 | height: 26px; 13 | } 14 | .jq-schedule .sc_menu .sc_header_cell { 15 | float: left; 16 | } 17 | .jq-schedule .sc_menu .sc_header { 18 | float: left; 19 | height: 26px; 20 | position: relative; 21 | overflow: hidden; 22 | } 23 | .jq-schedule .sc_menu .sc_header .sc_time { 24 | text-align: center; 25 | border-left: solid 1px #fff; 26 | background: #555; 27 | } 28 | .jq-schedule .sc_menu .sc_header_cell, 29 | .jq-schedule .sc_data { 30 | float: left; 31 | font-weight: bold; 32 | color: #fff; 33 | background: #555; 34 | position: relative; 35 | } 36 | .jq-schedule .sc_menu .sc_header_scroll, 37 | .jq-schedule .sc_data .sc_data_scroll { 38 | position: absolute; 39 | left: 0; 40 | top: 0; 41 | } 42 | .jq-schedule .sc_menu .sc_header_cell, 43 | .jq-schedule .sc_header .sc_time, 44 | .jq-schedule .sc_main_scroll .sc_time { 45 | color: #fff; 46 | padding: 0; 47 | line-height: 26px; 48 | height: 26px; 49 | display: block; 50 | } 51 | .jq-schedule .sc_header .sc_time, 52 | .jq-schedule .sc_main_scroll .sc_time { 53 | float: left; 54 | } 55 | .jq-schedule .sc_main_box, 56 | .jq-schedule .sc_data { 57 | max-height: 500px; 58 | overflow: hidden; 59 | } 60 | .jq-schedule .sc_main_box { 61 | float: left; 62 | overflow: auto; 63 | } 64 | .jq-schedule .sc_main { 65 | position: relative; 66 | } 67 | .jq-schedule .timeline { 68 | position: relative; 69 | } 70 | .jq-schedule .ui-draggable-dragging, 71 | .jq-schedule .ui-resizeable { 72 | z-index: 20; 73 | } 74 | .jq-schedule .sc_bar { 75 | position: absolute; 76 | color: #fff; 77 | background: #4f93d6; 78 | cursor: pointer; 79 | z-index: 10; 80 | } 81 | .jq-schedule .sc_bar .head { 82 | display: block; 83 | margin-top: 6px; 84 | font-size: 12px; 85 | padding: 0 14px; 86 | height: 1.2em; 87 | overflow: hidden; 88 | } 89 | .jq-schedule .sc_bar .text { 90 | display: block; 91 | margin-top: 6px; 92 | font-weight: bold; 93 | padding: 0 14px; 94 | height: 1.2em; 95 | overflow: hidden; 96 | } 97 | .jq-schedule .sc_bar .ui-resizable-handle { 98 | display: block; 99 | content: " "; 100 | position: absolute; 101 | height: 100%; 102 | right: 0; 103 | top: 0; 104 | width: 5px; 105 | background: rgb(45.7235023041, 121.5576036866, 196.2764976959); 106 | } 107 | .jq-schedule .sc_bar .ui-resizable-handle.ui-resizable-e { 108 | right: 0; 109 | } 110 | .jq-schedule .sc_bar .ui-resizable-handle.ui-resizable-w { 111 | left: 0; 112 | } 113 | .jq-schedule .timeline, 114 | .jq-schedule .sc_main .tb { 115 | border-bottom: solid 2px #666; 116 | } 117 | .jq-schedule .sc_data .timeline { 118 | overflow: hidden; 119 | padding: 10px; 120 | } 121 | .jq-schedule .sc_data .timeline span { 122 | display: block; 123 | } 124 | .jq-schedule .sc_data .timeline span.timeline-subtitle { 125 | font-size: 0.8em; 126 | color: #cccccc; 127 | } 128 | .jq-schedule .sc_main_scroll .sc_main .tl { 129 | float: left; 130 | height: 100%; 131 | border-right: solid 1px #ccc; 132 | } 133 | .jq-schedule .sc_main_scroll .sc_main .tl:hover { 134 | background: #f0f0f0; 135 | } 136 | .jq-schedule .ui-state-disabled { 137 | opacity: 1; 138 | } 139 | .jq-schedule .ui-state-disabled .ui-resizable-handle { 140 | display: none; 141 | } 142 | .jq-schedule .ui-state-disabled .ui-resizable-handle:hover { 143 | cursor: auto; 144 | } 145 | .jq-schedule .ui-draggable-disabled { 146 | opacity: 0.8; 147 | } -------------------------------------------------------------------------------- /src/sass/style.scss: -------------------------------------------------------------------------------- 1 | // variable 2 | $schedule-bg-color: #f0f0f0; 3 | $schedule-border-color: #ccc; 4 | $schedule-header-height: 26px; 5 | $schedule-header-bg-color: #555; 6 | $schedule-header-color: #fff; 7 | $schedule-box-max-height: 500px; 8 | $schedule-box-header-color: #fff; 9 | $schedule-box-header-bg-color: #555; 10 | $schedule-bar-color: #fff; 11 | $schedule-bar-bg-color: #4f93d6; 12 | $schedule-bar-shadow-enable: false; 13 | $schedule-bar-shadow-color: #333; 14 | $schedule-bar-padding: 14px; 15 | 16 | @mixin clearfix() { 17 | // *zoom: 1; 18 | &::after { 19 | content: ""; 20 | display: table; 21 | clear: both; 22 | } 23 | } 24 | 25 | // styles 26 | .jq-schedule { 27 | * { 28 | box-sizing: border-box; 29 | } 30 | 31 | .sc_wrapper, 32 | .sc_menu { 33 | @include clearfix; 34 | } 35 | 36 | .sc_menu { 37 | width: 100%; 38 | height: $schedule-header-height; 39 | 40 | .sc_header_cell { 41 | float: left; 42 | } 43 | 44 | .sc_header { 45 | float: left; 46 | height: $schedule-header-height; 47 | position: relative; 48 | overflow: hidden; 49 | 50 | .sc_time { 51 | text-align: center; 52 | border-left: solid 1px $schedule-header-color; 53 | background: $schedule-header-bg-color; 54 | } 55 | } 56 | } 57 | 58 | .sc_menu .sc_header_cell, 59 | .sc_data { 60 | float: left; 61 | font-weight: bold; 62 | color: $schedule-box-header-color; 63 | background: $schedule-box-header-bg-color; 64 | position: relative; 65 | } 66 | 67 | .sc_menu .sc_header_scroll, 68 | .sc_data .sc_data_scroll { 69 | position: absolute; 70 | left: 0; 71 | top: 0; 72 | } 73 | 74 | .sc_menu .sc_header_cell, 75 | .sc_header .sc_time, 76 | .sc_main_scroll .sc_time { 77 | color: $schedule-bar-color; 78 | padding: 0; 79 | line-height: $schedule-header-height; 80 | height: $schedule-header-height; 81 | display: block; 82 | } 83 | 84 | .sc_header .sc_time, 85 | .sc_main_scroll .sc_time { 86 | float: left; 87 | } 88 | 89 | .sc_main_box, 90 | .sc_data { 91 | max-height: $schedule-box-max-height; 92 | overflow: hidden; 93 | } 94 | 95 | .sc_main_box { 96 | float: left; 97 | overflow: auto; 98 | } 99 | 100 | .sc_main { 101 | position: relative; 102 | } 103 | 104 | .timeline { 105 | position: relative; 106 | } 107 | 108 | .ui-draggable-dragging, 109 | .ui-resizeable { 110 | z-index: 20; 111 | } 112 | 113 | .sc_bar { 114 | position: absolute; 115 | color: $schedule-bar-color; 116 | background: $schedule-bar-bg-color; 117 | cursor: pointer; 118 | z-index: 10; 119 | 120 | @if $schedule-bar-shadow-enable { 121 | box-shadow: 2px 2px 4px $schedule-bar-shadow-color; 122 | } 123 | 124 | .head { 125 | display: block; 126 | margin-top: 6px; 127 | font-size: 12px; 128 | padding: 0 $schedule-bar-padding; 129 | height: 1.2em; 130 | overflow: hidden; 131 | } 132 | 133 | .text { 134 | display: block; 135 | margin-top: 6px; 136 | font-weight: bold; 137 | padding: 0 $schedule-bar-padding; 138 | height: 1.2em; 139 | overflow: hidden; 140 | } 141 | 142 | .ui-resizable-handle { 143 | display: block; 144 | content: " "; 145 | position: absolute; 146 | height: 100%; 147 | right: 0; 148 | top: 0; 149 | width: 5px; 150 | background: darken($schedule-bar-bg-color, 10%); 151 | 152 | &.ui-resizable-e { 153 | right: 0; 154 | } 155 | 156 | &.ui-resizable-w { 157 | left: 0; 158 | } 159 | } 160 | } 161 | 162 | .timeline, 163 | .sc_main .tb { 164 | border-bottom: solid 2px #666; 165 | } 166 | 167 | .sc_data { 168 | .timeline { 169 | overflow: hidden; 170 | padding: 10px; 171 | 172 | span { 173 | display: block; 174 | 175 | &.timeline-subtitle { 176 | font-size: 0.8em; 177 | color: darken($schedule-bar-color, 20%); 178 | } 179 | } 180 | } 181 | } 182 | 183 | .sc_main_scroll .sc_main { 184 | .tl { 185 | float: left; 186 | height: 100%; 187 | border-right: solid 1px $schedule-border-color; 188 | 189 | &:hover { 190 | background: $schedule-bg-color; 191 | } 192 | } 193 | } 194 | 195 | .ui-state-disabled { 196 | opacity: 1; 197 | 198 | .ui-resizable-handle { 199 | display: none; 200 | 201 | &:hover { 202 | cursor: auto; 203 | } 204 | } 205 | } 206 | 207 | .ui-draggable-disabled { 208 | opacity: 0.8; 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | jq.Schedule 2 | =============== 3 | 4 | [![npm version](https://badge.fury.io/js/jq.schedule.svg)](https://badge.fury.io/js/jq.schedule) 5 | [![CI](https://github.com/ateliee/jquery.schedule/workflows/CI/badge.svg)](https://github.com/ateliee/jquery.schedule/actions) 6 | ![License: MIT](https://img.shields.io/badge/License-MIT-green.svg) 7 | 8 | ![](docs/img/preview.png) 9 | 10 | jquery and html schedule calendar 11 | 12 | * Drag and Drop Support 13 | * Resize Schedule 14 | * Ajax Get Data 15 | 16 | ## install 17 | 18 | ### package 19 | ``` 20 | npm i jq.schedule --save 21 | ``` 22 | 23 | ## Demo 24 | 25 | [Try Demo](https://ateliee.github.io/jquery.schedule/demo/) 26 | 27 | ## How to use 28 | 29 | append head css 30 | ``` 31 | 32 | ``` 33 | 34 | insert body 35 | ``` 36 |
37 | 38 | 103 | ``` 104 | 105 | ## Options 106 | 107 | ### Paramaters 108 | 109 | |Key|Value|Description| 110 | |---|------|----------| 111 | |className|jq-schedule|add elemnt class(default jq.schedule)| 112 | |startTime|07:00|schedule start time(HH:ii)| 113 | |endTime|21:00|schedule end time(HH:ii)| 114 | |widthTime|600|cell timestamp example 10 minutes| 115 | |timeLineY|60|height(px)| 116 | |verticalScrollbar|20|scrollbar (px)| 117 | |timeLineBorder|2|border(top and bottom)| 118 | |bundleMoveWidth|6|width to move all schedules to the right of the clicked time line cell| 119 | |rows|{object}|schedule data| 120 | |draggable|{boolean}|enable draggable(default true)| 121 | |resizable|{boolean}|enable resizable(default true)| 122 | |resizableLeft|{boolean}|enable left handle resizable(default false)| 123 | 124 | ### Schedule Data 125 | 126 | |Key|Type|Description| 127 | |---|----|-----| 128 | |title|string|Schedule Row Title| 129 | |subtitle|string|Schedule Row Description| 130 | |schedule|object[]|schedule row of array| 131 | 132 | ### Schedule Data in Object 133 | 134 | |Key|Type|Description| 135 | |---|----|-----| 136 | |start|string|HH:ii| 137 | |end|string|HH:ii| 138 | |text|string|Bar Title| 139 | |data|object|bind data| 140 | 141 | ### Callback Methods 142 | 143 | #### onChange(node: Element, data: Object) 144 | on change schedule bar callback 145 | 146 | #### onInitRow(node: Element, data: Object) 147 | initialize data 148 | 149 | #### onClick(node: Element, data: Object) 150 | on click bar callback 151 | 152 | #### onAppendRow(node: Element, data: Object) 153 | on add schedule row callback 154 | 155 | #### onSppendSchedule(node: Element, data: Object) 156 | on add schedule bar callback 157 | 158 | #### onScheduleClick(node: Element, time: string, data: Object) 159 | on click schedule row callback 160 | 161 | ### Methods 162 | 163 | #### get row data 164 | ``` 165 | var data = $("#schedule").timeSchedule('timelineData'); 166 | ``` 167 | 168 | #### get schedule data 169 | ``` 170 | var data = $("#schedule").timeSchedule('scheduleData'); 171 | ``` 172 | 173 | #### clear all data 174 | ``` 175 | $("#schedule").timeSchedule('resetData'); 176 | ``` 177 | 178 | #### clear all data and row 179 | ``` 180 | $("#schedule").timeSchedule('resetRowData'); 181 | ``` 182 | 183 | #### add row 184 | ``` 185 | $("#schedule").timeSchedule('addRow', timeline, { 186 | title : 'Title Area', 187 | schedule:[ 188 | { 189 | start:'09:00', 190 | end:'12:00', 191 | text:'Text Area', 192 | data:{ 193 | } 194 | } 195 | ] 196 | }); 197 | ``` 198 | 199 | #### add data 200 | ``` 201 | $("#schedule").timeSchedule('addSchedule', timeline, { 202 | start: start, 203 | end: end, 204 | text:'Insert Schedule', 205 | data:{} 206 | }); 207 | ``` 208 | 209 | #### switch setting 210 | ``` 211 | $("#schedule").timeSchedule('setDraggable', true or false); 212 | ``` 213 | ``` 214 | $("#schedule").timeSchedule('setResizable', true or false); 215 | ``` 216 | 217 | ## For Development 218 | 219 | run server browser-sync 220 | ``` 221 | npm run serve 222 | ``` 223 | 224 | -------------------------------------------------------------------------------- /dist/js/jq.schedule.min.js: -------------------------------------------------------------------------------- 1 | "use strict";!function(f){const t="jqSchedule";var _={calcStringTime:function(e){e=e.split(":");return 60*Number(e[0])*60+60*Number(e[1])},formatTime:function(e){var t=e%3600;return""+Math.floor(e/36e3)+Math.floor(e/3600%10)+":"+(""+Math.floor(t/600)+Math.floor(t/60%10))},_saveSettingData:function(e){return this.data(t+"Setting",e)},_loadSettingData:function(){return this.data(t+"Setting")},_saveData:function(e){e=f.extend({tableStartTime:0,tableEndTime:0,schedule:[],timeline:[]},e);return this.data(t,e)},_loadData:function(){return this.data(t)},scheduleData:function(){var e=f(this),e=_._loadData.apply(e);return e?e.schedule:[]},timelineData:function(){var e=f(this),t=_._loadData.apply(e);let i=[],a;for(a in t.timeline)i[a]=t.timeline[a],i[a].schedule=[];for(a in t.schedule){var n=t.schedule[a];void 0!==n.timeline&&void 0!==i[n.timeline]&&i[n.timeline].schedule.push(n)}return i},resetData:function(){return this.each(function(){let e=f(this),t=_._loadData.apply(e);for(var i in t.schedule=[],_._saveData.apply(e,[t]),e.find(".sc_bar").remove(),t.timeline)t.timeline[i].schedule=[],_._resizeRow.apply(e,[i,0]);_._saveData.apply(e,[t])})},addSchedule:function(i,a){return this.each(function(){var e=f(this);let t={start:a.start,end:a.end,startTime:_.calcStringTime(a.start),endTime:_.calcStringTime(a.end),text:a.text,timeline:i};a.data&&(t.data=a.data),_._addScheduleData.apply(e,[i,t]),_._resetBarPosition.apply(e,[i])})},addRow:function(t,i){return this.each(function(){var e=f(this);_._addRow.apply(e,[t,i])})},resetRowData:function(){return this.each(function(){let e=f(this),t=_._loadData.apply(e);t.schedule=[],t.timeline=[],_._saveData.apply(e,[t]),e.find(".sc_bar").remove(),e.find(".timeline").remove(),e.find(".sc_data").height(0)})},setRows:function(i){return this.each(function(){var e,t=f(this);for(e in _.resetRowData.apply(t,[]),i)_.addRow.apply(t,[e,i[e]])})},setDraggable:function(i){return this.each(function(){let e=f(this),t=_._loadSettingData.apply(e);i!==t.draggable&&(t.draggable=i,_._saveSettingData.apply(e,t),i?e.find(".sc_bar").draggable("enable"):e.find(".sc_bar").draggable("disable"))})},setResizable:function(i){return this.each(function(){let e=f(this),t=_._loadSettingData.apply(e);i!==t.resizable&&(t.resizable=i,_._saveSettingData.apply(e,t),i?e.find(".sc_bar").resizable("enable"):e.find(".sc_bar").resizable("disable"))})},_getTimeLineNumber:function(t,e){var i=f(this),a=_._loadSettingData.apply(i);let n=0,l=0;var s,d=Math.ceil(e/(a.timeLineY+a.timeLinePaddingTop+a.timeLinePaddingBottom));for(s in a.rows){var r=a.rows[s];let e=0;if("object"==typeof r.schedule&&(e=r.schedule.length),t&&t.timeline&&e++,(l+=Math.max(e,1))>=d)break;n++}return n},_addScheduleBgData:function(l){return this.each(function(){let e=f(this);var t=_._loadSettingData.apply(e),i=_._loadData.apply(e),a=Math.ceil((l.startTime-i.tableStartTime)/t.widthTime),i=Math.floor((l.endTime-i.tableStartTime)/t.widthTime);let n=f('
');n.css({left:a*t.widthTimeX,top:0,width:(i-a)*t.widthTimeX,height:e.find(".sc_main .timeline").eq(l.timeline).height()}),l.text&&n.find(".text").text(l.text),l.class&&n.addClass(l.class),e.find(".sc_main .timeline").eq(l.timeline).append(n)})},_addScheduleData:function(h,e){let m=e;return m.startTime=m.startTime||_.calcStringTime(m.start),m.endTime=m.endTime||_.calcStringTime(m.end),this.each(function(){let l=f(this),s=_._loadSettingData.apply(l),d=_._loadData.apply(l);var e=Math.ceil((m.startTime-d.tableStartTime)/s.widthTime),t=Math.floor((m.endTime-d.tableStartTime)/s.widthTime);let i=f('
');var a=_.formatTime(m.startTime),n=_.formatTime(m.endTime),r=_._getScheduleCount.apply(l,[m.timeline]);i.css({left:e*s.widthTimeX,top:r*s.timeLineY+s.timeLinePaddingTop,width:(t-e)*s.widthTimeX,height:s.timeLineY}),i.find(".time").text(a+"-"+n),m.text&&i.find(".text").text(m.text),m.class&&i.addClass(m.class);l.find(".sc_main .timeline").eq(h).append(i),d.schedule.push(m),_._saveData.apply(l,[d]),s.onAppendSchedule&&s.onAppendSchedule.apply(l,[i,m]);r=d.schedule.length-1,i.data("sc_key",r),i.on("mouseup",function(){if(s.onClick&&!0!==f(this).data("dragCheck")&&!0!==f(this).data("resizeCheck")){let e=f(this);var t=e.data("sc_key");s.onClick.apply(l,[e,d.schedule[t]])}}),t=l.find(".sc_bar");let o=null,c=(t.draggable({grid:[s.widthTimeX,1],containment:l.find(".sc_main"),helper:"original",start:function(e,t){let i={};i.node=this,i.offsetTop=t.position.top,i.offsetLeft=t.position.left,i.currentTop=t.position.top,i.currentLeft=t.position.left,i.timeline=_._getTimeLineNumber.apply(l,[o,t.position.top]),i.nowTimeline=i.timeline,o=i},drag:function(e,t){if(f(this).data("dragCheck",!0),!o)return!1;let i=f(this);var a=i.data("sc_key"),n=_._getTimeLineNumber.apply(l,[o,t.position.top]);return t.position.left=Math.floor(t.position.left/s.widthTimeX)*s.widthTimeX,o.nowTimeline!==n&&(o.nowTimeline=n),o.currentTop=t.position.top,o.currentLeft=t.position.left,_._rewriteBarText.apply(l,[i,d.schedule[a]]),!0},stop:function(){f(this).data("dragCheck",!1),o=null;let e=f(this);var t=e.data("sc_key"),i=e.position().left,i=d.tableStartTime+Math.floor(i/s.widthTimeX)*s.widthTime,a=i+(d.schedule[t].endTime-d.schedule[t].startTime);d.schedule[t].start=_.formatTime(i),d.schedule[t].end=_.formatTime(a),d.schedule[t].startTime=i,d.schedule[t].endTime=a,s.onChange&&s.onChange.apply(l,[e,d.schedule[t]])}}),["e"]);return s.resizableLeft&&c.push("w"),t.resizable({handles:c.join(","),grid:[s.widthTimeX,s.timeLineY-s.timeBorder],minWidth:s.widthTimeX,containment:l.find(".sc_main_scroll"),start:function(){let e=f(this);e.data("resizeCheck",!0)},resize:function(e,t){t.element.height(t.size.height),t.element.width(t.size.width)},stop:function(){let e=f(this);var t=e.data("sc_key"),i=e.position().left,a=e.outerWidth(),n=d.tableStartTime+Math.floor(i/s.widthTimeX)*s.widthTime,i=d.tableStartTime+Math.floor((i+a)/s.widthTimeX)*s.widthTime,a=d.schedule[t].timeline;d.schedule[t].start=_.formatTime(n),d.schedule[t].end=_.formatTime(i),d.schedule[t].startTime=n,d.schedule[t].endTime=i,_._resetBarPosition.apply(l,[a]),_._rewriteBarText.apply(l,[e,d.schedule[t]]),e.data("resizeCheck",!1),s.onChange&&s.onChange.apply(l,[e,d.schedule[t]])}}),!1===s.draggable&&t.draggable("disable"),!1===s.resizable&&t.resizable("disable"),r})},_getScheduleCount:function(e){var t,i=f(this),a=_._loadData.apply(i);let n=0;for(t in a.schedule)a.schedule[t].timeline===e&&n++;return n},_addRow:function(p,u){return this.each(function(){let l=f(this),i=_._loadSettingData.apply(l),s=_._loadData.apply(l);var e=l.find(".sc_main .timeline").length,t="";let a=f(t+='
'),n=(u.title&&a.append(''+u.title+""),u.subtitle&&a.append(''+u.subtitle+""),i.onInitRow&&i.onInitRow.apply(l,[a,u]),l.find(".sc_data_scroll").append(a),t="",f(t+='
'));for(var d=s.tableStartTime;d');r.outerWidth(i.widthTimeX),r.data("time",_.formatTime(d)),r.data("timeline",p),n.append(r)}if(n.find(".tl").on("click",function(){i.onScheduleClick&&i.onScheduleClick.apply(l,[this,f(this).data("time"),f(this).data("timeline"),s.timeline[f(this).data("timeline")]])}),n.find(".tl").on("contextmenu",function(){return i.onScheduleClick&&i.onScheduleClick.apply(l,[this,f(this).data("time"),f(this).data("timeline"),s.timeline[f(this).data("timeline")]]),!1}),l.find(".sc_main").append(n),s.timeline[p]=u,_._saveData.apply(l,[s]),u.class&&""!==u.class&&(l.find(".sc_data .timeline").eq(e).addClass(u.class),l.find(".sc_main .timeline").eq(e).addClass(u.class)),u.schedule)for(var o in u.schedule){var o=u.schedule[o],c=o.start||_.calcStringTime(o.startTime),h=o.end||_.calcStringTime(o.endTime),m={};m.start=c,m.end=h,o.text&&(m.text=o.text),m.timeline=p,m.data={},o.data&&(m.data=o.data),_._addScheduleData.apply(l,[e,m])}_._resetBarPosition.apply(l,[e]),l.find(".sc_main .timeline").eq(e).droppable({accept:".sc_bar",drop:function(e,t){let i=t.draggable;var t=i.data("sc_key"),a=s.schedule[t].timeline,n=l.find(".sc_main .timeline").index(this);s.schedule[t].timeline=n,i.appendTo(this),_._resetBarPosition.apply(l,[a]),_._resetBarPosition.apply(l,[n])}}),i.onAppendRow&&l.find(".sc_main .timeline").eq(e).find(".sc_bar").each(function(){let e=f(this);var t=e.data("sc_key");i.onAppendRow.apply(l,[e,s.schedule[t]])})})},_rewriteBarText:function(a,n){return this.each(function(){var e=f(this),t=_._loadSettingData.apply(e),e=_._loadData.apply(e),i=a.position().left,e=e.tableStartTime+Math.floor(i/t.widthTimeX)*t.widthTime,i=e+(n.endTime-n.startTime),t=_.formatTime(e)+"-"+_.formatTime(i);f(a).find(".time").html(t)})},_resetBarPosition:function(u){return this.each(function(){let e=f(this);var t,i,a,n,l=_._loadSettingData.apply(e),s=e.find(".sc_main .timeline").eq(u).find(".sc_bar");let d=[],r=[],o=0,c,h,m;for(m=0;mt.x?1:0}),m=0;m\n
 
\n
\n
\n
\n\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
'),i.addClass(a.className),i.find(".sc_main_box").on("scroll",function(){i.find(".sc_data_scroll").css("top",-1*f(this).scrollTop()),i.find(".sc_header_scroll").css("left",-1*f(this).scrollLeft())});let s=-1;for(let t=n;t'+_.formatTime(t)+"";let e=f(d);var d=Number(Math.min(3600*Math.ceil((t+a.widthTime)/3600),l)-t),d=Math.floor(d/a.widthTime);e.width(d*a.widthTimeX),i.find(".sc_header_scroll").append(e),s=t}for(e in f(window).on("resize",function(){_._resizeWindow.apply(i)}).trigger("resize"),a.rows)_._addRow.apply(i,[e,a.rows[e]])})}};f.fn.timeSchedule=function(e){return _[e]?_[e].apply(this,Array.prototype.slice.call(arguments,1)):"object"!=typeof e&&e?(f.error("Method "+e+" does not exist on jQuery.timeSchedule"),this):_.init.apply(this,arguments)}}(jQuery); 2 | //# sourceMappingURL=jq.schedule.min.js.map 3 | -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | jq.Schedule Demo 6 | 7 | 8 | 9 | 10 | 11 | 12 | 53 | 54 | 55 | 56 | 77 |
78 |
79 |

jq.Schedule

80 |

javascript base time schedule plugin.

81 |

82 | Drag and Drop Support/Resize Schedule/Ajax Support(Callback Event Option) 83 |

84 |
85 | Loading version... 86 |
87 |
88 |
89 | 90 | 91 |
92 |
93 |

Demo

94 |

95 | Example Sample Demo 96 |

97 |

Method

98 |
99 | 100 | 101 | 102 | 103 | 104 | 105 |
106 |

Ajax

107 |
108 | 109 | 110 | 111 |
112 |
113 |
114 |
115 |
116 |

Log

117 |
118 |
119 | clear 120 |
121 |
122 |
123 |
124 |
125 |
126 |

Get Start

127 |
    128 |
  1. Please Download jQuery.Schedule

  2. 129 |
  3. 130 |

    include jquery.jsandjquery.ui.js after jq.schedule

    131 |
    <script src="jquery-3.4.1.js" type="text/javascript" language="javascript"></script>
    132 | <script src="jquery-ui-2.3.2.js" type="text/javascript" language="javascript"></script>
    133 | <script type="text/javascript" src="../dist/js/jq.schedule.js"></script>
    134 | <link rel="stylesheet" type="text/css" href="../dist/css/style.min.css" />
    135 |
  4. 136 |
  5. 137 |

    init scripts

    138 |
    <script type="text/javascript">
    139 | $(function(){
    140 |     var $sc = jQuery("#schedule").timeSchedule($options);
    141 | });
    142 | </script>
    143 |
  6. 144 |
145 | 146 |

Option

147 |
148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 |
nameTypedescription
classNameStringadd elemnt class(default jq.schedule)
startTimeString(HH:ii)schedule start time
endTimeString(HH:ii)schedule end time
widthTimeIntegerminuite time line cell
timeLineYIntegercell height px
verticalScrollbarIntegerscrollbar (px)
timeLineBorderIntegerborder(top and bottom)
bundleMoveWidthIntegerwidth to move all schedules to the right of the clicked time line cell. 193 |
draggableBooleanenable draggable
resizableBooleanenable resizable
resizableLeftBooleanenable left handle resizable(default false)
rowsArraySchedule Data
titleStringSchedule Left Cell Text
subtitleString(Option)Schedule Left Cell Sub Text
scheduleArraySchedule Cell Data
onInitRowfunctioncallback init time
onChangefunctioncallback change time
onClickfunctioncallback click time
onScheduleClickfunctioncallback timebar click
onAppendRowfunctioncallback append time
onAppendSchedulefunctioncallback append time data
262 |
263 | 264 |

Schedule Data

265 |
266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 |
nameTypedescription
titleStringVisible Text Schedule Bar
classStringadd class name
schedulearrayadd schedule data
startStringtime String(HH:ii)
endStringtime String(HH:ii)
textStringShow Text
dataObject,Arraycallback data Object
308 |
309 |
310 | 311 | 312 | 317 | 318 | 322 | 323 | 324 | 325 | 326 | 461 | 462 | 463 | -------------------------------------------------------------------------------- /dist/js/jq.schedule.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | (function ($) { 4 | 'use strict'; 5 | 6 | const PLUGIN_NAME = 'jqSchedule'; 7 | var methods = { 8 | /** 9 | * 10 | * @param {string} str 11 | * @returns {number} 12 | */ 13 | calcStringTime: function (str) { 14 | let slice = str.split(':'); 15 | let h = Number(slice[0]) * 60 * 60; 16 | let i = Number(slice[1]) * 60; 17 | return h + i; 18 | }, 19 | /** 20 | * 21 | * @param {number} val 22 | * @returns {string} 23 | */ 24 | formatTime: function (val) { 25 | let i1 = val % 3600; 26 | let h = '' + Math.floor(val / 36000) + Math.floor(val / 3600 % 10); 27 | let i = '' + Math.floor(i1 / 600) + Math.floor(i1 / 60 % 10); 28 | return h + ':' + i; 29 | }, 30 | /** 31 | * 設定データの保存 32 | * 33 | * @param {Options} data 34 | * @returns {*} 35 | */ 36 | _saveSettingData: function (data) { 37 | return this.data(PLUGIN_NAME + 'Setting', data); 38 | }, 39 | /** 40 | * 設定データの取得 41 | * 42 | * @returns Options 43 | */ 44 | _loadSettingData: function () { 45 | return this.data(PLUGIN_NAME + 'Setting'); 46 | }, 47 | /** 48 | * 保存データの保存 49 | * 50 | * @param {SaveData} data 51 | * @returns {*} 52 | */ 53 | _saveData: function (data) { 54 | let d = $.extend({ 55 | tableStartTime: 0, 56 | tableEndTime: 0, 57 | schedule: [], 58 | timeline: [] 59 | }, data); 60 | return this.data(PLUGIN_NAME, d); 61 | }, 62 | /** 63 | * 保存データの取得 64 | * 65 | * @returns SaveData 66 | */ 67 | _loadData: function () { 68 | return this.data(PLUGIN_NAME); 69 | }, 70 | /** 71 | * スケジュールの取得 72 | * 73 | * @returns ScheduleData[] 74 | */ 75 | scheduleData: function () { 76 | let $this = $(this); 77 | let saveData = methods._loadData.apply($this); 78 | if (saveData) { 79 | return saveData.schedule; 80 | } 81 | return []; 82 | }, 83 | /** 84 | * get timelineData 85 | * @returns {any[]} 86 | */ 87 | timelineData: function () { 88 | let $this = $(this); 89 | let saveData = methods._loadData.apply($this); 90 | let data = []; 91 | let i; 92 | for (i in saveData.timeline) { 93 | data[i] = saveData.timeline[i]; 94 | data[i].schedule = []; 95 | } 96 | for (i in saveData.schedule) { 97 | var d = saveData.schedule[i]; 98 | if (typeof d.timeline === 'undefined') { 99 | continue; 100 | } 101 | if (typeof data[d.timeline] === 'undefined') { 102 | continue; 103 | } 104 | data[d.timeline].schedule.push(d); 105 | } 106 | return data; 107 | }, 108 | /** 109 | * reset data 110 | */ 111 | resetData: function () { 112 | return this.each(function () { 113 | let $this = $(this); 114 | let saveData = methods._loadData.apply($this); 115 | saveData.schedule = []; 116 | methods._saveData.apply($this, [saveData]); 117 | $this.find('.sc_bar').remove(); 118 | for (var i in saveData.timeline) { 119 | saveData.timeline[i].schedule = []; 120 | methods._resizeRow.apply($this, [i, 0]); 121 | } 122 | methods._saveData.apply($this, [saveData]); 123 | }); 124 | }, 125 | /** 126 | * add schedule data 127 | * 128 | * @param {number} timeline 129 | * @param {object} data 130 | * @returns {methods} 131 | */ 132 | addSchedule: function (timeline, data) { 133 | return this.each(function () { 134 | let $this = $(this); 135 | let d = { 136 | start: data.start, 137 | end: data.end, 138 | startTime: methods.calcStringTime(data.start), 139 | endTime: methods.calcStringTime(data.end), 140 | text: data.text, 141 | timeline: timeline 142 | }; 143 | if (data.data) { 144 | d.data = data.data; 145 | } 146 | methods._addScheduleData.apply($this, [timeline, d]); 147 | methods._resetBarPosition.apply($this, [timeline]); 148 | }); 149 | }, 150 | /** 151 | * add schedule data 152 | * 153 | * @param {number} timeline 154 | * @param {object} data 155 | * @returns {methods} 156 | */ 157 | addRow: function (timeline, data) { 158 | return this.each(function () { 159 | let $this = $(this); 160 | methods._addRow.apply($this, [timeline, data]); 161 | }); 162 | }, 163 | /** 164 | * clear row 165 | * 166 | * @returns {methods} 167 | */ 168 | resetRowData: function () { 169 | return this.each(function () { 170 | let $this = $(this); 171 | let data = methods._loadData.apply($this); 172 | data.schedule = []; 173 | data.timeline = []; 174 | methods._saveData.apply($this, [data]); 175 | $this.find('.sc_bar').remove(); 176 | $this.find('.timeline').remove(); 177 | $this.find('.sc_data').height(0); 178 | }); 179 | }, 180 | /** 181 | * clear row 182 | * 183 | * @param {object} data 184 | * @returns {methods} 185 | */ 186 | setRows: function (data) { 187 | return this.each(function () { 188 | let $this = $(this); 189 | methods.resetRowData.apply($this, []); 190 | for (var timeline in data) { 191 | methods.addRow.apply($this, [timeline, data[timeline]]); 192 | } 193 | }); 194 | }, 195 | /** 196 | * switch draggable 197 | * @param {boolean} enable 198 | */ 199 | setDraggable: function (enable) { 200 | return this.each(function () { 201 | let $this = $(this); 202 | let setting = methods._loadSettingData.apply($this); 203 | if (enable !== setting.draggable) { 204 | setting.draggable = enable; 205 | methods._saveSettingData.apply($this, setting); 206 | if (enable) { 207 | $this.find('.sc_bar').draggable('enable'); 208 | } else { 209 | $this.find('.sc_bar').draggable('disable'); 210 | } 211 | } 212 | }); 213 | }, 214 | /** 215 | * switch resizable 216 | * @param {boolean} enable 217 | */ 218 | setResizable: function (enable) { 219 | return this.each(function () { 220 | let $this = $(this); 221 | let setting = methods._loadSettingData.apply($this); 222 | if (enable !== setting.resizable) { 223 | setting.resizable = enable; 224 | methods._saveSettingData.apply($this, setting); 225 | if (enable) { 226 | $this.find('.sc_bar').resizable('enable'); 227 | } else { 228 | $this.find('.sc_bar').resizable('disable'); 229 | } 230 | } 231 | }); 232 | }, 233 | /** 234 | * 現在のタイムライン番号を取得 235 | * 236 | * @param node 237 | * @param top 238 | * @returns {number} 239 | */ 240 | _getTimeLineNumber: function (node, top) { 241 | let $this = $(this); 242 | let setting = methods._loadSettingData.apply($this); 243 | let num = 0; 244 | let n = 0; 245 | let tn = Math.ceil(top / (setting.timeLineY + setting.timeLinePaddingTop + setting.timeLinePaddingBottom)); 246 | for (var i in setting.rows) { 247 | let r = setting.rows[i]; 248 | let tr = 0; 249 | if (typeof r.schedule === 'object') { 250 | tr = r.schedule.length; 251 | } 252 | if (node && node.timeline) { 253 | tr++; 254 | } 255 | n += Math.max(tr, 1); 256 | if (n >= tn) { 257 | break; 258 | } 259 | num++; 260 | } 261 | return num; 262 | }, 263 | /** 264 | * 背景データ追加 265 | * 266 | * @param {ScheduleData} data 267 | */ 268 | _addScheduleBgData: function (data) { 269 | return this.each(function () { 270 | let $this = $(this); 271 | let setting = methods._loadSettingData.apply($this); 272 | let saveData = methods._loadData.apply($this); 273 | let st = Math.ceil((data.startTime - saveData.tableStartTime) / setting.widthTime); 274 | let et = Math.floor((data.endTime - saveData.tableStartTime) / setting.widthTime); 275 | let $bar = $('
'); 276 | $bar.css({ 277 | left: st * setting.widthTimeX, 278 | top: 0, 279 | width: (et - st) * setting.widthTimeX, 280 | height: $this.find('.sc_main .timeline').eq(data.timeline).height() 281 | }); 282 | if (data.text) { 283 | $bar.find('.text').text(data.text); 284 | } 285 | if (data.class) { 286 | $bar.addClass(data.class); 287 | } 288 | // $element.find('.sc_main').append($bar); 289 | $this.find('.sc_main .timeline').eq(data.timeline).append($bar); 290 | }); 291 | }, 292 | /** 293 | * スケジュール追加 294 | * 295 | * @param timeline 296 | * @param {ScheduleData} d 297 | * @returns {number} 298 | */ 299 | _addScheduleData: function (timeline, d) { 300 | let data = d; 301 | data.startTime = data.startTime ? data.startTime : methods.calcStringTime(data.start); 302 | data.endTime = data.endTime ? data.endTime : methods.calcStringTime(data.end); 303 | return this.each(function () { 304 | let $this = $(this); 305 | let setting = methods._loadSettingData.apply($this); 306 | let saveData = methods._loadData.apply($this); 307 | let st = Math.ceil((data.startTime - saveData.tableStartTime) / setting.widthTime); 308 | let et = Math.floor((data.endTime - saveData.tableStartTime) / setting.widthTime); 309 | let $bar = $('
'); 310 | let stext = methods.formatTime(data.startTime); 311 | let etext = methods.formatTime(data.endTime); 312 | let snum = methods._getScheduleCount.apply($this, [data.timeline]); 313 | $bar.css({ 314 | left: st * setting.widthTimeX, 315 | top: snum * setting.timeLineY + setting.timeLinePaddingTop, 316 | width: (et - st) * setting.widthTimeX, 317 | height: setting.timeLineY 318 | }); 319 | $bar.find('.time').text(stext + '-' + etext); 320 | if (data.text) { 321 | $bar.find('.text').text(data.text); 322 | } 323 | if (data.class) { 324 | $bar.addClass(data.class); 325 | } 326 | // $this.find('.sc_main').append($bar); 327 | var $row = $this.find('.sc_main .timeline').eq(timeline); 328 | $row.append($bar); 329 | // データの追加 330 | saveData.schedule.push(data); 331 | methods._saveData.apply($this, [saveData]); 332 | // コールバックがセットされていたら呼出 333 | if (setting.onAppendSchedule) { 334 | setting.onAppendSchedule.apply($this, [$bar, data]); 335 | } 336 | // key 337 | var key = saveData.schedule.length - 1; 338 | $bar.data('sc_key', key); 339 | $bar.on('mouseup', function () { 340 | // コールバックがセットされていたら呼出 341 | if (setting.onClick) { 342 | if ($(this).data('dragCheck') !== true && $(this).data('resizeCheck') !== true) { 343 | let $n = $(this); 344 | let scKey = $n.data('sc_key'); 345 | setting.onClick.apply($this, [$n, saveData.schedule[scKey]]); 346 | } 347 | } 348 | }); 349 | var $node = $this.find('.sc_bar'); 350 | let currentNode = null; 351 | // move node. 352 | $node.draggable({ 353 | grid: [setting.widthTimeX, 1], 354 | containment: $this.find('.sc_main'), 355 | helper: 'original', 356 | start: function (event, ui) { 357 | let node = {}; 358 | node.node = this; 359 | node.offsetTop = ui.position.top; 360 | node.offsetLeft = ui.position.left; 361 | node.currentTop = ui.position.top; 362 | node.currentLeft = ui.position.left; 363 | node.timeline = methods._getTimeLineNumber.apply($this, [currentNode, ui.position.top]); 364 | node.nowTimeline = node.timeline; 365 | currentNode = node; 366 | }, 367 | /** 368 | * 369 | * @param {Event} event 370 | * @param {function} ui 371 | * @returns {boolean} 372 | */ 373 | drag: function (event, ui) { 374 | $(this).data('dragCheck', true); 375 | if (!currentNode) { 376 | return false; 377 | } 378 | let $moveNode = $(this); 379 | let scKey = $moveNode.data('sc_key'); 380 | let timelineNum = methods._getTimeLineNumber.apply($this, [currentNode, ui.position.top]); 381 | // eslint-disable-next-line no-param-reassign 382 | ui.position.left = Math.floor(ui.position.left / setting.widthTimeX) * setting.widthTimeX; 383 | if (currentNode.nowTimeline !== timelineNum) { 384 | // 現在のタイムライン 385 | currentNode.nowTimeline = timelineNum; 386 | } 387 | currentNode.currentTop = ui.position.top; 388 | currentNode.currentLeft = ui.position.left; 389 | // テキスト変更 390 | methods._rewriteBarText.apply($this, [$moveNode, saveData.schedule[scKey]]); 391 | return true; 392 | }, 393 | // 要素の移動が終った後の処理 394 | stop: function () { 395 | $(this).data('dragCheck', false); 396 | currentNode = null; 397 | let $n = $(this); 398 | let scKey = $n.data('sc_key'); 399 | let x = $n.position().left; 400 | // var w = $n.width(); 401 | let start = saveData.tableStartTime + Math.floor(x / setting.widthTimeX) * setting.widthTime; 402 | // var end = saveData.tableStartTime + (Math.floor((x + w) / setting.widthTimeX) * setting.widthTime); 403 | let end = start + (saveData.schedule[scKey].endTime - saveData.schedule[scKey].startTime); 404 | saveData.schedule[scKey].start = methods.formatTime(start); 405 | saveData.schedule[scKey].end = methods.formatTime(end); 406 | saveData.schedule[scKey].startTime = start; 407 | saveData.schedule[scKey].endTime = end; 408 | // コールバックがセットされていたら呼出 409 | if (setting.onChange) { 410 | setting.onChange.apply($this, [$n, saveData.schedule[scKey]]); 411 | } 412 | } 413 | }); 414 | let resizableHandles = ['e']; 415 | if (setting.resizableLeft) { 416 | resizableHandles.push('w'); 417 | } 418 | $node.resizable({ 419 | handles: resizableHandles.join(','), 420 | grid: [setting.widthTimeX, setting.timeLineY - setting.timeBorder], 421 | minWidth: setting.widthTimeX, 422 | containment: $this.find('.sc_main_scroll'), 423 | start: function () { 424 | let $n = $(this); 425 | $n.data('resizeCheck', true); 426 | }, 427 | resize: function (ev, ui) { 428 | // box-sizing: border-box; に対応 429 | ui.element.height(ui.size.height); 430 | ui.element.width(ui.size.width); 431 | }, 432 | // 要素の移動が終った後の処理 433 | stop: function () { 434 | let $n = $(this); 435 | let scKey = $n.data('sc_key'); 436 | let x = $n.position().left; 437 | let w = $n.outerWidth(); 438 | let start = saveData.tableStartTime + Math.floor(x / setting.widthTimeX) * setting.widthTime; 439 | let end = saveData.tableStartTime + Math.floor((x + w) / setting.widthTimeX) * setting.widthTime; 440 | let timelineNum = saveData.schedule[scKey].timeline; 441 | saveData.schedule[scKey].start = methods.formatTime(start); 442 | saveData.schedule[scKey].end = methods.formatTime(end); 443 | saveData.schedule[scKey].startTime = start; 444 | saveData.schedule[scKey].endTime = end; 445 | 446 | // 高さ調整 447 | methods._resetBarPosition.apply($this, [timelineNum]); 448 | // テキスト変更 449 | methods._rewriteBarText.apply($this, [$n, saveData.schedule[scKey]]); 450 | $n.data('resizeCheck', false); 451 | // コールバックがセットされていたら呼出 452 | if (setting.onChange) { 453 | setting.onChange.apply($this, [$n, saveData.schedule[scKey]]); 454 | } 455 | } 456 | }); 457 | if (setting.draggable === false) { 458 | $node.draggable('disable'); 459 | } 460 | if (setting.resizable === false) { 461 | $node.resizable('disable'); 462 | } 463 | return key; 464 | }); 465 | }, 466 | /** 467 | * スケジュール数の取得 468 | * 469 | * @param {number} n row number 470 | * @returns {number} 471 | */ 472 | _getScheduleCount: function (n) { 473 | let $this = $(this); 474 | let saveData = methods._loadData.apply($this); 475 | let num = 0; 476 | for (let i in saveData.schedule) { 477 | if (saveData.schedule[i].timeline === n) { 478 | num++; 479 | } 480 | } 481 | return num; 482 | }, 483 | /** 484 | * add rows 485 | * 486 | * @param timeline 487 | * @param row 488 | */ 489 | _addRow: function (timeline, row) { 490 | return this.each(function () { 491 | let $this = $(this); 492 | let setting = methods._loadSettingData.apply($this); 493 | let saveData = methods._loadData.apply($this); 494 | let id = $this.find('.sc_main .timeline').length; 495 | let html; 496 | html = ''; 497 | html += '
'; 498 | let $data = $(html); 499 | if (row.title) { 500 | $data.append('' + row.title + ''); 501 | } 502 | if (row.subtitle) { 503 | $data.append('' + row.subtitle + ''); 504 | } 505 | // event call 506 | if (setting.onInitRow) { 507 | setting.onInitRow.apply($this, [$data, row]); 508 | } 509 | $this.find('.sc_data_scroll').append($data); 510 | html = ''; 511 | html += '
'; 512 | let $timeline = $(html); 513 | for (var t = saveData.tableStartTime; t < saveData.tableEndTime; t += setting.widthTime) { 514 | var $tl = $('
'); 515 | $tl.outerWidth(setting.widthTimeX); 516 | $tl.data('time', methods.formatTime(t)); 517 | $tl.data('timeline', timeline); 518 | $timeline.append($tl); 519 | } 520 | // クリックイベント 521 | // left click 522 | $timeline.find('.tl').on('click', function () { 523 | if (setting.onScheduleClick) { 524 | setting.onScheduleClick.apply($this, [this, $(this).data('time'), $(this).data('timeline'), saveData.timeline[$(this).data('timeline')]]); 525 | } 526 | }); 527 | // right click 528 | $timeline.find('.tl').on('contextmenu', function () { 529 | if (setting.onScheduleClick) { 530 | setting.onScheduleClick.apply($this, [this, $(this).data('time'), $(this).data('timeline'), saveData.timeline[$(this).data('timeline')]]); 531 | } 532 | return false; 533 | }); 534 | $this.find('.sc_main').append($timeline); 535 | saveData.timeline[timeline] = row; 536 | methods._saveData.apply($this, [saveData]); 537 | if (row.class && row.class !== '') { 538 | $this.find('.sc_data .timeline').eq(id).addClass(row.class); 539 | $this.find('.sc_main .timeline').eq(id).addClass(row.class); 540 | } 541 | // スケジュールタイムライン 542 | if (row.schedule) { 543 | for (var i in row.schedule) { 544 | var bdata = row.schedule[i]; 545 | var s = bdata.start ? bdata.start : methods.calcStringTime(bdata.startTime); 546 | var e = bdata.end ? bdata.end : methods.calcStringTime(bdata.endTime); 547 | var data = {}; 548 | data.start = s; 549 | data.end = e; 550 | if (bdata.text) { 551 | data.text = bdata.text; 552 | } 553 | data.timeline = timeline; 554 | data.data = {}; 555 | if (bdata.data) { 556 | data.data = bdata.data; 557 | } 558 | methods._addScheduleData.apply($this, [id, data]); 559 | } 560 | } 561 | // 高さの調整 562 | methods._resetBarPosition.apply($this, [id]); 563 | $this.find('.sc_main .timeline').eq(id).droppable({ 564 | accept: '.sc_bar', 565 | drop: function (ev, ui) { 566 | let node = ui.draggable; 567 | let scKey = node.data('sc_key'); 568 | let nowTimelineNum = saveData.schedule[scKey].timeline; 569 | let timelineNum = $this.find('.sc_main .timeline').index(this); 570 | // タイムラインの変更 571 | saveData.schedule[scKey].timeline = timelineNum; 572 | node.appendTo(this); 573 | // 高さ調整 574 | methods._resetBarPosition.apply($this, [nowTimelineNum]); 575 | methods._resetBarPosition.apply($this, [timelineNum]); 576 | } 577 | }); 578 | // コールバックがセットされていたら呼出 579 | if (setting.onAppendRow) { 580 | $this.find('.sc_main .timeline').eq(id).find('.sc_bar').each(function () { 581 | let $n = $(this); 582 | let scKey = $n.data('sc_key'); 583 | setting.onAppendRow.apply($this, [$n, saveData.schedule[scKey]]); 584 | }); 585 | } 586 | }); 587 | }, 588 | /** 589 | * テキストの変更 590 | * 591 | * @param {jQuery} node 592 | * @param {Object} data 593 | */ 594 | _rewriteBarText: function (node, data) { 595 | return this.each(function () { 596 | let $this = $(this); 597 | let setting = methods._loadSettingData.apply($this); 598 | let saveData = methods._loadData.apply($this); 599 | let x = node.position().left; 600 | // var w = node.width(); 601 | let start = saveData.tableStartTime + Math.floor(x / setting.widthTimeX) * setting.widthTime; 602 | // var end = saveData.tableStartTime + (Math.floor((x + w) / setting.widthTimeX) * setting.widthTime); 603 | let end = start + (data.endTime - data.startTime); 604 | let html = methods.formatTime(start) + '-' + methods.formatTime(end); 605 | $(node).find('.time').html(html); 606 | }); 607 | }, 608 | /** 609 | * 610 | * @param {Number} n 611 | */ 612 | _resetBarPosition: function (n) { 613 | return this.each(function () { 614 | let $this = $(this); 615 | let setting = methods._loadSettingData.apply($this); 616 | // 要素の並び替え 617 | let $barList = $this.find('.sc_main .timeline').eq(n).find('.sc_bar'); 618 | let codes = [], 619 | check = []; 620 | let h = 0; 621 | let $e1, $e2; 622 | let c1, c2, s1, s2, e1, e2; 623 | let i; 624 | for (i = 0; i < $barList.length; i++) { 625 | codes[i] = { 626 | code: i, 627 | x: $($barList[i]).position().left 628 | }; 629 | } 630 | // ソート 631 | codes.sort(function (a, b) { 632 | if (a.x < b.x) { 633 | return -1; 634 | } 635 | if (a.x > b.x) { 636 | return 1; 637 | } 638 | return 0; 639 | }); 640 | for (i = 0; i < codes.length; i++) { 641 | c1 = codes[i].code; 642 | $e1 = $($barList[c1]); 643 | for (h = 0; h < check.length; h++) { 644 | let next = false; 645 | for (var j = 0; j < check[h].length; j++) { 646 | c2 = check[h][j]; 647 | $e2 = $($barList[c2]); 648 | s1 = $e1.position().left; 649 | e1 = $e1.position().left + $e1.outerWidth(); 650 | s2 = $e2.position().left; 651 | e2 = $e2.position().left + $e2.outerWidth(); 652 | if (s1 < e2 && e1 > s2) { 653 | next = true; 654 | continue; 655 | } 656 | } 657 | if (!next) { 658 | break; 659 | } 660 | } 661 | if (!check[h]) { 662 | check[h] = []; 663 | } 664 | $e1.css({ 665 | top: h * setting.timeLineY + setting.timeLinePaddingTop 666 | }); 667 | check[h][check[h].length] = c1; 668 | } 669 | // 高さの調整 670 | methods._resizeRow.apply($this, [n, check.length]); 671 | }); 672 | }, 673 | /** 674 | * 675 | * @param n 676 | * @param height 677 | */ 678 | _resizeRow: function (n, height) { 679 | return this.each(function () { 680 | let $this = $(this); 681 | let setting = methods._loadSettingData.apply($this); 682 | let h = Math.max(height, 1); 683 | $this.find('.sc_data .timeline').eq(n).outerHeight(h * setting.timeLineY + setting.timeLineBorder + setting.timeLinePaddingTop + setting.timeLinePaddingBottom); 684 | $this.find('.sc_main .timeline').eq(n).outerHeight(h * setting.timeLineY + setting.timeLineBorder + setting.timeLinePaddingTop + setting.timeLinePaddingBottom); 685 | $this.find('.sc_main .timeline').eq(n).find('.sc_bgBar').each(function () { 686 | $(this).outerHeight($(this).closest('.timeline').outerHeight()); 687 | }); 688 | $this.find('.sc_data').outerHeight($this.find('.sc_main_box').outerHeight()); 689 | }); 690 | }, 691 | /** 692 | * resizeWindow 693 | */ 694 | _resizeWindow: function () { 695 | return this.each(function () { 696 | let $this = $(this); 697 | let setting = methods._loadSettingData.apply($this); 698 | let saveData = methods._loadData.apply($this); 699 | let scWidth = $this.width(); 700 | let scMainWidth = scWidth - setting.dataWidth - setting.verticalScrollbar; 701 | let cellNum = Math.floor((saveData.tableEndTime - saveData.tableStartTime) / setting.widthTime); 702 | $this.find('.sc_header_cell').width(setting.dataWidth); 703 | $this.find('.sc_data,.sc_data_scroll').width(setting.dataWidth); 704 | $this.find('.sc_header').width(scMainWidth); 705 | $this.find('.sc_main_box').width(scMainWidth); 706 | $this.find('.sc_header_scroll').width(setting.widthTimeX * cellNum); 707 | $this.find('.sc_main_scroll').width(setting.widthTimeX * cellNum); 708 | }); 709 | }, 710 | /** 711 | * move all cells of the right of the specified time line cell 712 | * 713 | * @param timeline 714 | * @param baseTimeLineCell 715 | * @param moveWidth 716 | */ 717 | _moveSchedules: function (timeline, baseTimeLineCell, moveWidth) { 718 | return this.each(function () { 719 | let $this = $(this); 720 | let setting = methods._loadSettingData.apply($this); 721 | let saveData = methods._loadData.apply($this); 722 | let $barList = $this.find('.sc_main .timeline').eq(timeline).find('.sc_bar'); 723 | for (let i = 0; i < $barList.length; i++) { 724 | let $bar = $($barList[i]); 725 | if (baseTimeLineCell.position().left <= $bar.position().left) { 726 | let v1 = $bar.position().left + setting.widthTimeX * moveWidth; 727 | let v2 = Math.floor((saveData.tableEndTime - saveData.tableStartTime) / setting.widthTime) * setting.widthTimeX - $bar.outerWidth(); 728 | $bar.css({ 729 | left: Math.max(0, Math.min(v1, v2)) 730 | }); 731 | let scKey = $bar.data('sc_key'); 732 | let start = saveData.tableStartTime + Math.floor($bar.position().left / setting.widthTimeX) * setting.widthTime; 733 | let end = start + (saveData.schedule[scKey].end - saveData.schedule[scKey].start); 734 | saveData.schedule[scKey].start = methods.formatTime(start); 735 | saveData.schedule[scKey].end = methods.formatTime(end); 736 | saveData.schedule[scKey].startTime = start; 737 | saveData.schedule[scKey].endTime = end; 738 | methods._rewriteBarText.apply($this, [$bar, saveData.schedule[scKey]]); 739 | 740 | // if setting 741 | if (setting.onChange) { 742 | setting.onChange.apply($this, [$bar, saveData.schedule[scKey]]); 743 | } 744 | } 745 | } 746 | methods._resetBarPosition.apply($this, [timeline]); 747 | }); 748 | }, 749 | /** 750 | * initialize 751 | */ 752 | init: function (options) { 753 | return this.each(function () { 754 | let $this = $(this); 755 | let config = $.extend({ 756 | className: 'jq-schedule', 757 | rows: {}, 758 | startTime: '07:00', 759 | endTime: '19:30', 760 | widthTimeX: 25, 761 | // 1cell辺りの幅(px) 762 | widthTime: 600, 763 | // 区切り時間(秒) 764 | timeLineY: 50, 765 | // timeline height(px) 766 | timeLineBorder: 1, 767 | // timeline height border 768 | timeBorder: 1, 769 | // border width 770 | timeLinePaddingTop: 0, 771 | timeLinePaddingBottom: 0, 772 | headTimeBorder: 1, 773 | // time border width 774 | dataWidth: 160, 775 | // data width 776 | verticalScrollbar: 0, 777 | // vertical scrollbar width 778 | bundleMoveWidth: 1, 779 | // width to move all schedules to the right of the clicked time cell 780 | draggable: true, 781 | resizable: true, 782 | resizableLeft: false, 783 | // event 784 | onInitRow: null, 785 | onChange: null, 786 | onClick: null, 787 | onAppendRow: null, 788 | onAppendSchedule: null, 789 | onScheduleClick: null 790 | }, options); 791 | methods._saveSettingData.apply($this, [config]); 792 | let tableStartTime = methods.calcStringTime(config.startTime); 793 | let tableEndTime = methods.calcStringTime(config.endTime); 794 | tableStartTime -= tableStartTime % config.widthTime; 795 | tableEndTime -= tableEndTime % config.widthTime; 796 | methods._saveData.apply($this, [{ 797 | tableStartTime: tableStartTime, 798 | tableEndTime: tableEndTime 799 | }]); 800 | let html = '' + '
' + '\n' + '
 
' + '\n' + '
' + '\n' + '
' + '\n' + '
' + '\n' + '
' + '\n' + '
' + '\n' + '
' + '\n' + '
' + '\n' + '
' + '\n' + '
' + '\n' + '
' + '\n' + '
' + '\n' + '
' + '\n' + '
' + '\n' + '
'; 801 | $this.append(html); 802 | $this.addClass(config.className); 803 | $this.find('.sc_main_box').on('scroll', function () { 804 | $this.find('.sc_data_scroll').css('top', $(this).scrollTop() * -1); 805 | $this.find('.sc_header_scroll').css('left', $(this).scrollLeft() * -1); 806 | }); 807 | // add time cell 808 | // var cellNum = Math.floor((tableEndTime - tableStartTime) / config.widthTime); 809 | let beforeTime = -1; 810 | for (let t = tableStartTime; t < tableEndTime; t += config.widthTime) { 811 | if (beforeTime < 0 || Math.floor(beforeTime / 3600) !== Math.floor(t / 3600)) { 812 | html = ''; 813 | html += '
' + methods.formatTime(t) + '
'; 814 | let $time = $(html); 815 | let cn = Number(Math.min(Math.ceil((t + config.widthTime) / 3600) * 3600, tableEndTime) - t); 816 | let cellNum = Math.floor(cn / config.widthTime); 817 | $time.width(cellNum * config.widthTimeX); 818 | $this.find('.sc_header_scroll').append($time); 819 | beforeTime = t; 820 | } 821 | } 822 | $(window).on('resize', function () { 823 | methods._resizeWindow.apply($this); 824 | }).trigger('resize'); 825 | 826 | // addrow 827 | for (let i in config.rows) { 828 | methods._addRow.apply($this, [i, config.rows[i]]); 829 | } 830 | }); 831 | } 832 | }; 833 | /** 834 | * 835 | * @param {Object|string} method 836 | * @returns {jQuery|methods|*} 837 | */ 838 | // eslint-disable-next-line no-param-reassign 839 | $.fn.timeSchedule = function (method) { 840 | // Method calling logic 841 | if (methods[method]) { 842 | return methods[method].apply(this, Array.prototype.slice.call(arguments, 1)); 843 | // eslint-disable-next-line no-else-return 844 | } else if (typeof method === 'object' || !method) { 845 | return methods.init.apply(this, arguments); 846 | } 847 | $.error('Method ' + method + ' does not exist on jQuery.timeSchedule'); 848 | return this; 849 | }; 850 | })(jQuery); -------------------------------------------------------------------------------- /src/js/jq.schedule.js: -------------------------------------------------------------------------------- 1 | (function ($) { 2 | 'use strict'; 3 | 4 | const PLUGIN_NAME = 'jqSchedule'; 5 | 6 | var methods = { 7 | /** 8 | * 9 | * @param {string} str 10 | * @returns {number} 11 | */ 12 | calcStringTime: function (str) { 13 | let slice = str.split(':'); 14 | let h = Number(slice[0]) * 60 * 60; 15 | let i = Number(slice[1]) * 60; 16 | return h + i; 17 | }, 18 | /** 19 | * 20 | * @param {number} val 21 | * @returns {string} 22 | */ 23 | formatTime: function (val) { 24 | let i1 = (val % 3600); 25 | let h = '' + Math.floor(val / 36000) + Math.floor((val / 3600) % 10); 26 | let i = '' + Math.floor(i1 / 600) + Math.floor((i1 / 60) % 10); 27 | return h + ':' + i; 28 | }, 29 | 30 | /** 31 | * 設定データの保存 32 | * 33 | * @param {Options} data 34 | * @returns {*} 35 | */ 36 | _saveSettingData: function (data) { 37 | return this.data(PLUGIN_NAME + 'Setting', data); 38 | }, 39 | 40 | /** 41 | * 設定データの取得 42 | * 43 | * @returns Options 44 | */ 45 | _loadSettingData: function () { 46 | return this.data(PLUGIN_NAME + 'Setting'); 47 | }, 48 | 49 | /** 50 | * 保存データの保存 51 | * 52 | * @param {SaveData} data 53 | * @returns {*} 54 | */ 55 | _saveData: function (data) { 56 | let d = $.extend({ 57 | tableStartTime: 0, 58 | tableEndTime: 0, 59 | schedule: [], 60 | timeline: [] 61 | }, data); 62 | return this.data(PLUGIN_NAME, d); 63 | }, 64 | 65 | /** 66 | * 保存データの取得 67 | * 68 | * @returns SaveData 69 | */ 70 | _loadData: function () { 71 | return this.data(PLUGIN_NAME); 72 | }, 73 | 74 | /** 75 | * スケジュールの取得 76 | * 77 | * @returns ScheduleData[] 78 | */ 79 | scheduleData: function () { 80 | let $this = $(this); 81 | let saveData = methods._loadData.apply($this); 82 | if (saveData) { 83 | return saveData.schedule; 84 | } 85 | return []; 86 | }, 87 | /** 88 | * get timelineData 89 | * @returns {any[]} 90 | */ 91 | timelineData: function () { 92 | let $this = $(this); 93 | let saveData = methods._loadData.apply($this); 94 | let data = []; 95 | let i; 96 | for (i in saveData.timeline) { 97 | data[i] = saveData.timeline[i]; 98 | data[i].schedule = []; 99 | } 100 | for (i in saveData.schedule) { 101 | var d = saveData.schedule[i]; 102 | if (typeof d.timeline === 'undefined') { 103 | continue; 104 | } 105 | if (typeof data[d.timeline] === 'undefined') { 106 | continue; 107 | } 108 | data[d.timeline].schedule.push(d); 109 | } 110 | return data; 111 | }, 112 | /** 113 | * reset data 114 | */ 115 | resetData: function () { 116 | return this.each(function () { 117 | let $this = $(this); 118 | let saveData = methods._loadData.apply($this); 119 | saveData.schedule = []; 120 | methods._saveData.apply($this, [saveData]); 121 | 122 | $this.find('.sc_bar').remove(); 123 | for (var i in saveData.timeline) { 124 | saveData.timeline[i].schedule = []; 125 | methods._resizeRow.apply($this, [i, 0]); 126 | } 127 | methods._saveData.apply($this, [saveData]); 128 | }); 129 | }, 130 | /** 131 | * add schedule data 132 | * 133 | * @param {number} timeline 134 | * @param {object} data 135 | * @returns {methods} 136 | */ 137 | addSchedule: function (timeline, data) { 138 | return this.each(function () { 139 | let $this = $(this); 140 | let d = { 141 | start: data.start, 142 | end: data.end, 143 | startTime: methods.calcStringTime(data.start), 144 | endTime: methods.calcStringTime(data.end), 145 | text: data.text, 146 | timeline: timeline 147 | }; 148 | if (data.data) { 149 | d.data = data.data; 150 | } 151 | methods._addScheduleData.apply($this, [timeline, d]); 152 | methods._resetBarPosition.apply($this, [timeline]); 153 | }); 154 | }, 155 | /** 156 | * add schedule data 157 | * 158 | * @param {number} timeline 159 | * @param {object} data 160 | * @returns {methods} 161 | */ 162 | addRow: function (timeline, data) { 163 | return this.each(function () { 164 | let $this = $(this); 165 | methods._addRow.apply($this, [timeline, data]); 166 | }); 167 | }, 168 | /** 169 | * clear row 170 | * 171 | * @returns {methods} 172 | */ 173 | resetRowData: function () { 174 | return this.each(function () { 175 | let $this = $(this); 176 | let data = methods._loadData.apply($this); 177 | data.schedule = []; 178 | data.timeline = []; 179 | methods._saveData.apply($this, [data]); 180 | $this.find('.sc_bar').remove(); 181 | $this.find('.timeline').remove(); 182 | $this.find('.sc_data').height(0); 183 | }); 184 | }, 185 | /** 186 | * clear row 187 | * 188 | * @param {object} data 189 | * @returns {methods} 190 | */ 191 | setRows: function (data) { 192 | return this.each(function () { 193 | let $this = $(this); 194 | methods.resetRowData.apply($this, []); 195 | for (var timeline in data) { 196 | methods.addRow.apply($this, [timeline, data[timeline]]); 197 | } 198 | }); 199 | }, 200 | /** 201 | * switch draggable 202 | * @param {boolean} enable 203 | */ 204 | setDraggable: function (enable) { 205 | return this.each(function () { 206 | let $this = $(this); 207 | let setting = methods._loadSettingData.apply($this); 208 | if (enable !== setting.draggable) { 209 | setting.draggable = enable; 210 | methods._saveSettingData.apply($this, setting); 211 | if (enable) { 212 | $this.find('.sc_bar').draggable('enable'); 213 | } else { 214 | $this.find('.sc_bar').draggable('disable'); 215 | } 216 | } 217 | }); 218 | }, 219 | /** 220 | * switch resizable 221 | * @param {boolean} enable 222 | */ 223 | setResizable: function (enable) { 224 | return this.each(function () { 225 | let $this = $(this); 226 | let setting = methods._loadSettingData.apply($this); 227 | if (enable !== setting.resizable) { 228 | setting.resizable = enable; 229 | methods._saveSettingData.apply($this, setting); 230 | if (enable) { 231 | $this.find('.sc_bar').resizable('enable'); 232 | } else { 233 | $this.find('.sc_bar').resizable('disable'); 234 | } 235 | } 236 | }); 237 | }, 238 | /** 239 | * 現在のタイムライン番号を取得 240 | * 241 | * @param node 242 | * @param top 243 | * @returns {number} 244 | */ 245 | _getTimeLineNumber: function (node, top) { 246 | let $this = $(this); 247 | let setting = methods._loadSettingData.apply($this); 248 | let num = 0; 249 | let n = 0; 250 | let tn = Math.ceil(top / (setting.timeLineY + setting.timeLinePaddingTop + setting.timeLinePaddingBottom)); 251 | for (var i in setting.rows) { 252 | let r = setting.rows[i]; 253 | let tr = 0; 254 | if (typeof r.schedule === 'object') { 255 | tr = r.schedule.length; 256 | } 257 | if (node && node.timeline) { 258 | tr++; 259 | } 260 | n += Math.max(tr, 1); 261 | if (n >= tn) { 262 | break; 263 | } 264 | num++; 265 | } 266 | return num; 267 | }, 268 | /** 269 | * 背景データ追加 270 | * 271 | * @param {ScheduleData} data 272 | */ 273 | _addScheduleBgData: function (data) { 274 | return this.each(function () { 275 | let $this = $(this); 276 | let setting = methods._loadSettingData.apply($this); 277 | let saveData = methods._loadData.apply($this); 278 | let st = Math.ceil((data.startTime - saveData.tableStartTime) / setting.widthTime); 279 | let et = Math.floor((data.endTime - saveData.tableStartTime) / setting.widthTime); 280 | let $bar = $('
'); 281 | $bar.css({ 282 | left: (st * setting.widthTimeX), 283 | top: 0, 284 | width: ((et - st) * setting.widthTimeX), 285 | height: $this.find('.sc_main .timeline').eq(data.timeline).height() 286 | }); 287 | if (data.text) { 288 | $bar.find('.text').text(data.text); 289 | } 290 | if (data.class) { 291 | $bar.addClass(data.class); 292 | } 293 | // $element.find('.sc_main').append($bar); 294 | $this.find('.sc_main .timeline').eq(data.timeline).append($bar); 295 | }); 296 | }, 297 | /** 298 | * スケジュール追加 299 | * 300 | * @param timeline 301 | * @param {ScheduleData} d 302 | * @returns {number} 303 | */ 304 | _addScheduleData: function (timeline, d) { 305 | let data = d; 306 | data.startTime = data.startTime ? data.startTime : methods.calcStringTime(data.start); 307 | data.endTime = data.endTime ? data.endTime : methods.calcStringTime(data.end); 308 | 309 | return this.each(function () { 310 | let $this = $(this); 311 | let setting = methods._loadSettingData.apply($this); 312 | let saveData = methods._loadData.apply($this); 313 | 314 | let st = Math.ceil((data.startTime - saveData.tableStartTime) / setting.widthTime); 315 | let et = Math.floor((data.endTime - saveData.tableStartTime) / setting.widthTime); 316 | let $bar = $('
'); 317 | let stext = methods.formatTime(data.startTime); 318 | let etext = methods.formatTime(data.endTime); 319 | let snum = methods._getScheduleCount.apply($this, [data.timeline]); 320 | $bar.css({ 321 | left: (st * setting.widthTimeX), 322 | top: ((snum * setting.timeLineY) + setting.timeLinePaddingTop), 323 | width: ((et - st) * setting.widthTimeX), 324 | height: (setting.timeLineY) 325 | }); 326 | $bar.find('.time').text(stext + '-' + etext); 327 | if (data.text) { 328 | $bar.find('.text').text(data.text); 329 | } 330 | if (data.class) { 331 | $bar.addClass(data.class); 332 | } 333 | // $this.find('.sc_main').append($bar); 334 | var $row = $this.find('.sc_main .timeline').eq(timeline); 335 | $row.append($bar); 336 | // データの追加 337 | saveData.schedule.push(data); 338 | methods._saveData.apply($this, [saveData]); 339 | // コールバックがセットされていたら呼出 340 | if (setting.onAppendSchedule) { 341 | setting.onAppendSchedule.apply($this, [ 342 | $bar, 343 | data 344 | ]); 345 | } 346 | // key 347 | var key = saveData.schedule.length - 1; 348 | $bar.data('sc_key', key); 349 | 350 | $bar.on('mouseup', function () { 351 | // コールバックがセットされていたら呼出 352 | if (setting.onClick) { 353 | if ($(this).data('dragCheck') !== true && $(this).data('resizeCheck') !== true) { 354 | let $n = $(this); 355 | let scKey = $n.data('sc_key'); 356 | setting.onClick.apply($this, [ 357 | $n, 358 | saveData.schedule[scKey] 359 | ]); 360 | } 361 | } 362 | }); 363 | 364 | var $node = $this.find('.sc_bar'); 365 | let currentNode = null; 366 | // move node. 367 | $node.draggable({ 368 | grid: [setting.widthTimeX, 1], 369 | containment: $this.find('.sc_main'), 370 | helper: 'original', 371 | start: function (event, ui) { 372 | let node = {}; 373 | node.node = this; 374 | node.offsetTop = ui.position.top; 375 | node.offsetLeft = ui.position.left; 376 | node.currentTop = ui.position.top; 377 | node.currentLeft = ui.position.left; 378 | node.timeline = methods._getTimeLineNumber.apply($this, [currentNode, ui.position.top]); 379 | node.nowTimeline = node.timeline; 380 | currentNode = node; 381 | }, 382 | /** 383 | * 384 | * @param {Event} event 385 | * @param {function} ui 386 | * @returns {boolean} 387 | */ 388 | drag: function (event, ui) { 389 | $(this).data('dragCheck', true); 390 | if (!currentNode) { 391 | return false; 392 | } 393 | let $moveNode = $(this); 394 | let scKey = $moveNode.data('sc_key'); 395 | let timelineNum = methods._getTimeLineNumber.apply($this, [currentNode, ui.position.top]); 396 | // eslint-disable-next-line no-param-reassign 397 | ui.position.left = Math.floor(ui.position.left / setting.widthTimeX) * setting.widthTimeX; 398 | 399 | if (currentNode.nowTimeline !== timelineNum) { 400 | // 現在のタイムライン 401 | currentNode.nowTimeline = timelineNum; 402 | } 403 | currentNode.currentTop = ui.position.top; 404 | currentNode.currentLeft = ui.position.left; 405 | // テキスト変更 406 | methods._rewriteBarText.apply($this, [$moveNode, saveData.schedule[scKey]]); 407 | return true; 408 | }, 409 | // 要素の移動が終った後の処理 410 | stop: function () { 411 | $(this).data('dragCheck', false); 412 | currentNode = null; 413 | 414 | let $n = $(this); 415 | let scKey = $n.data('sc_key'); 416 | let x = $n.position().left; 417 | // var w = $n.width(); 418 | let start = saveData.tableStartTime + (Math.floor(x / setting.widthTimeX) * setting.widthTime); 419 | // var end = saveData.tableStartTime + (Math.floor((x + w) / setting.widthTimeX) * setting.widthTime); 420 | let end = start + ((saveData.schedule[scKey].endTime - saveData.schedule[scKey].startTime)); 421 | 422 | saveData.schedule[scKey].start = methods.formatTime(start); 423 | saveData.schedule[scKey].end = methods.formatTime(end); 424 | saveData.schedule[scKey].startTime = start; 425 | saveData.schedule[scKey].endTime = end; 426 | // コールバックがセットされていたら呼出 427 | if (setting.onChange) { 428 | setting.onChange.apply($this, [ 429 | $n, 430 | saveData.schedule[scKey] 431 | ]); 432 | } 433 | } 434 | }); 435 | let resizableHandles = ['e']; 436 | if (setting.resizableLeft) { 437 | resizableHandles.push('w'); 438 | } 439 | $node.resizable({ 440 | handles: resizableHandles.join(','), 441 | grid: [setting.widthTimeX, setting.timeLineY - setting.timeBorder], 442 | minWidth: setting.widthTimeX, 443 | containment: $this.find('.sc_main_scroll'), 444 | start: function () { 445 | let $n = $(this); 446 | $n.data('resizeCheck', true); 447 | }, 448 | resize: function (ev, ui) { 449 | // box-sizing: border-box; に対応 450 | ui.element.height(ui.size.height); 451 | ui.element.width(ui.size.width); 452 | }, 453 | // 要素の移動が終った後の処理 454 | stop: function () { 455 | let $n = $(this); 456 | let scKey = $n.data('sc_key'); 457 | let x = $n.position().left; 458 | let w = $n.outerWidth(); 459 | let start = saveData.tableStartTime + (Math.floor(x / setting.widthTimeX) * setting.widthTime); 460 | let end = saveData.tableStartTime + (Math.floor((x + w) / setting.widthTimeX) * setting.widthTime); 461 | let timelineNum = saveData.schedule[scKey].timeline; 462 | 463 | saveData.schedule[scKey].start = methods.formatTime(start); 464 | saveData.schedule[scKey].end = methods.formatTime(end); 465 | saveData.schedule[scKey].startTime = start; 466 | saveData.schedule[scKey].endTime = end; 467 | 468 | // 高さ調整 469 | methods._resetBarPosition.apply($this, [timelineNum]); 470 | // テキスト変更 471 | methods._rewriteBarText.apply($this, [$n, saveData.schedule[scKey]]); 472 | 473 | $n.data('resizeCheck', false); 474 | // コールバックがセットされていたら呼出 475 | if (setting.onChange) { 476 | setting.onChange.apply($this, [ 477 | $n, 478 | saveData.schedule[scKey] 479 | ]); 480 | } 481 | } 482 | }); 483 | if (setting.draggable === false) { 484 | $node.draggable('disable'); 485 | } 486 | if (setting.resizable === false) { 487 | $node.resizable('disable'); 488 | } 489 | return key; 490 | }); 491 | }, 492 | /** 493 | * スケジュール数の取得 494 | * 495 | * @param {number} n row number 496 | * @returns {number} 497 | */ 498 | _getScheduleCount: function (n) { 499 | let $this = $(this); 500 | let saveData = methods._loadData.apply($this); 501 | let num = 0; 502 | for (let i in saveData.schedule) { 503 | if (saveData.schedule[i].timeline === n) { 504 | num++; 505 | } 506 | } 507 | return num; 508 | }, 509 | /** 510 | * add rows 511 | * 512 | * @param timeline 513 | * @param row 514 | */ 515 | _addRow: function (timeline, row) { 516 | return this.each(function () { 517 | let $this = $(this); 518 | let setting = methods._loadSettingData.apply($this); 519 | let saveData = methods._loadData.apply($this); 520 | let id = $this.find('.sc_main .timeline').length; 521 | 522 | let html; 523 | 524 | html = ''; 525 | html += '
'; 526 | let $data = $(html); 527 | if (row.title) { 528 | $data.append('' + row.title + ''); 529 | } 530 | if (row.subtitle) { 531 | $data.append('' + row.subtitle + ''); 532 | } 533 | // event call 534 | if (setting.onInitRow) { 535 | setting.onInitRow.apply($this, [ 536 | $data, 537 | row 538 | ]); 539 | } 540 | $this.find('.sc_data_scroll').append($data); 541 | 542 | html = ''; 543 | html += '
'; 544 | let $timeline = $(html); 545 | for (var t = saveData.tableStartTime; t < saveData.tableEndTime; t += setting.widthTime) { 546 | var $tl = $('
'); 547 | $tl.outerWidth(setting.widthTimeX); 548 | 549 | $tl.data('time', methods.formatTime(t)); 550 | $tl.data('timeline', timeline); 551 | $timeline.append($tl); 552 | } 553 | // クリックイベント 554 | // left click 555 | $timeline.find('.tl').on('click', function () { 556 | if (setting.onScheduleClick) { 557 | setting.onScheduleClick.apply($this, [ 558 | this, 559 | $(this).data('time'), 560 | $(this).data('timeline'), 561 | saveData.timeline[$(this).data('timeline')] 562 | ]); 563 | } 564 | }); 565 | // right click 566 | $timeline.find('.tl').on('contextmenu', function () { 567 | if (setting.onScheduleClick) { 568 | setting.onScheduleClick.apply($this, [ 569 | this, 570 | $(this).data('time'), 571 | $(this).data('timeline'), 572 | saveData.timeline[$(this).data('timeline')] 573 | ]); 574 | } 575 | return false; 576 | }); 577 | 578 | $this.find('.sc_main').append($timeline); 579 | 580 | saveData.timeline[timeline] = row; 581 | methods._saveData.apply($this, [saveData]); 582 | 583 | if (row.class && (row.class !== '')) { 584 | $this.find('.sc_data .timeline').eq(id).addClass(row.class); 585 | $this.find('.sc_main .timeline').eq(id).addClass(row.class); 586 | } 587 | // スケジュールタイムライン 588 | if (row.schedule) { 589 | for (var i in row.schedule) { 590 | var bdata = row.schedule[i]; 591 | var s = bdata.start ? bdata.start : methods.calcStringTime(bdata.startTime); 592 | var e = bdata.end ? bdata.end : methods.calcStringTime(bdata.endTime); 593 | 594 | var data = {}; 595 | data.start = s; 596 | data.end = e; 597 | if (bdata.text) { 598 | data.text = bdata.text; 599 | } 600 | data.timeline = timeline; 601 | data.data = {}; 602 | if (bdata.data) { 603 | data.data = bdata.data; 604 | } 605 | methods._addScheduleData.apply($this, [id, data]); 606 | } 607 | } 608 | // 高さの調整 609 | methods._resetBarPosition.apply($this, [id]); 610 | $this.find('.sc_main .timeline').eq(id).droppable({ 611 | accept: '.sc_bar', 612 | drop: function (ev, ui) { 613 | let node = ui.draggable; 614 | let scKey = node.data('sc_key'); 615 | let nowTimelineNum = saveData.schedule[scKey].timeline; 616 | let timelineNum = $this.find('.sc_main .timeline').index(this); 617 | // タイムラインの変更 618 | saveData.schedule[scKey].timeline = timelineNum; 619 | node.appendTo(this); 620 | // 高さ調整 621 | methods._resetBarPosition.apply($this, [nowTimelineNum]); 622 | methods._resetBarPosition.apply($this, [timelineNum]); 623 | } 624 | }); 625 | // コールバックがセットされていたら呼出 626 | if (setting.onAppendRow) { 627 | $this.find('.sc_main .timeline').eq(id).find('.sc_bar').each(function () { 628 | let $n = $(this); 629 | let scKey = $n.data('sc_key'); 630 | setting.onAppendRow.apply($this, [ 631 | $n, 632 | saveData.schedule[scKey] 633 | ]); 634 | }); 635 | } 636 | }); 637 | }, 638 | /** 639 | * テキストの変更 640 | * 641 | * @param {jQuery} node 642 | * @param {Object} data 643 | */ 644 | _rewriteBarText: function (node, data) { 645 | return this.each(function () { 646 | let $this = $(this); 647 | let setting = methods._loadSettingData.apply($this); 648 | let saveData = methods._loadData.apply($this); 649 | let x = node.position().left; 650 | // var w = node.width(); 651 | let start = saveData.tableStartTime + (Math.floor(x / setting.widthTimeX) * setting.widthTime); 652 | // var end = saveData.tableStartTime + (Math.floor((x + w) / setting.widthTimeX) * setting.widthTime); 653 | let end = start + (data.endTime - data.startTime); 654 | let html = methods.formatTime(start) + '-' + methods.formatTime(end); 655 | $(node).find('.time').html(html); 656 | }); 657 | }, 658 | /** 659 | * 660 | * @param {Number} n 661 | */ 662 | _resetBarPosition: function (n) { 663 | return this.each(function () { 664 | let $this = $(this); 665 | let setting = methods._loadSettingData.apply($this); 666 | // 要素の並び替え 667 | let $barList = $this.find('.sc_main .timeline').eq(n).find('.sc_bar'); 668 | let codes = [], check = []; 669 | let h = 0; 670 | let $e1, $e2; 671 | let c1, c2, s1, s2, e1, e2; 672 | let i; 673 | for (i = 0; i < $barList.length; i++) { 674 | codes[i] = { 675 | code: i, 676 | x: $($barList[i]).position().left 677 | }; 678 | } 679 | // ソート 680 | codes.sort(function (a, b) { 681 | if (a.x < b.x) { 682 | return -1; 683 | } 684 | if (a.x > b.x) { 685 | return 1; 686 | } 687 | return 0; 688 | }); 689 | for (i = 0; i < codes.length; i++) { 690 | c1 = codes[i].code; 691 | $e1 = $($barList[c1]); 692 | for (h = 0; h < check.length; h++) { 693 | let next = false; 694 | for (var j = 0; j < check[h].length; j++) { 695 | c2 = check[h][j]; 696 | $e2 = $($barList[c2]); 697 | 698 | s1 = $e1.position().left; 699 | e1 = $e1.position().left + $e1.outerWidth(); 700 | s2 = $e2.position().left; 701 | e2 = $e2.position().left + $e2.outerWidth(); 702 | if (s1 < e2 && e1 > s2) { 703 | next = true; 704 | continue; 705 | } 706 | } 707 | if (!next) { 708 | break; 709 | } 710 | } 711 | if (!check[h]) { 712 | check[h] = []; 713 | } 714 | $e1.css({ 715 | top: (h * setting.timeLineY) + setting.timeLinePaddingTop 716 | }); 717 | check[h][check[h].length] = c1; 718 | } 719 | // 高さの調整 720 | methods._resizeRow.apply($this, [n, check.length]); 721 | }); 722 | }, 723 | /** 724 | * 725 | * @param n 726 | * @param height 727 | */ 728 | _resizeRow: function (n, height) { 729 | return this.each(function () { 730 | let $this = $(this); 731 | let setting = methods._loadSettingData.apply($this); 732 | let h = Math.max(height, 1); 733 | $this.find('.sc_data .timeline').eq(n).outerHeight((h * setting.timeLineY) + setting.timeLineBorder + setting.timeLinePaddingTop + setting.timeLinePaddingBottom); 734 | $this.find('.sc_main .timeline').eq(n).outerHeight((h * setting.timeLineY) + setting.timeLineBorder + setting.timeLinePaddingTop + setting.timeLinePaddingBottom); 735 | 736 | $this.find('.sc_main .timeline').eq(n).find('.sc_bgBar').each(function () { 737 | $(this).outerHeight($(this).closest('.timeline').outerHeight()); 738 | }); 739 | 740 | $this.find('.sc_data').outerHeight($this.find('.sc_main_box').outerHeight()); 741 | }); 742 | }, 743 | /** 744 | * resizeWindow 745 | */ 746 | _resizeWindow: function () { 747 | return this.each(function () { 748 | let $this = $(this); 749 | let setting = methods._loadSettingData.apply($this); 750 | let saveData = methods._loadData.apply($this); 751 | let scWidth = $this.width(); 752 | let scMainWidth = scWidth - setting.dataWidth - (setting.verticalScrollbar); 753 | let cellNum = Math.floor((saveData.tableEndTime - saveData.tableStartTime) / setting.widthTime); 754 | $this.find('.sc_header_cell').width(setting.dataWidth); 755 | $this.find('.sc_data,.sc_data_scroll').width(setting.dataWidth); 756 | $this.find('.sc_header').width(scMainWidth); 757 | $this.find('.sc_main_box').width(scMainWidth); 758 | $this.find('.sc_header_scroll').width(setting.widthTimeX * cellNum); 759 | $this.find('.sc_main_scroll').width(setting.widthTimeX * cellNum); 760 | }); 761 | }, 762 | /** 763 | * move all cells of the right of the specified time line cell 764 | * 765 | * @param timeline 766 | * @param baseTimeLineCell 767 | * @param moveWidth 768 | */ 769 | _moveSchedules: function (timeline, baseTimeLineCell, moveWidth) { 770 | return this.each(function () { 771 | let $this = $(this); 772 | let setting = methods._loadSettingData.apply($this); 773 | let saveData = methods._loadData.apply($this); 774 | let $barList = $this.find('.sc_main .timeline').eq(timeline).find('.sc_bar'); 775 | for (let i = 0; i < $barList.length; i++) { 776 | let $bar = $($barList[i]); 777 | if (baseTimeLineCell.position().left <= $bar.position().left) { 778 | let v1 = $bar.position().left + setting.widthTimeX * moveWidth; 779 | let v2 = Math.floor((saveData.tableEndTime - saveData.tableStartTime) / setting.widthTime) * setting.widthTimeX - $bar.outerWidth(); 780 | $bar.css({ 781 | left: Math.max(0, Math.min(v1, v2)) 782 | }); 783 | 784 | let scKey = $bar.data('sc_key'); 785 | let start = saveData.tableStartTime + (Math.floor($bar.position().left / setting.widthTimeX) * setting.widthTime); 786 | let end = start + ((saveData.schedule[scKey].end - saveData.schedule[scKey].start)); 787 | saveData.schedule[scKey].start = methods.formatTime(start); 788 | saveData.schedule[scKey].end = methods.formatTime(end); 789 | saveData.schedule[scKey].startTime = start; 790 | saveData.schedule[scKey].endTime = end; 791 | methods._rewriteBarText.apply($this, [$bar, saveData.schedule[scKey]]); 792 | 793 | // if setting 794 | if (setting.onChange) { 795 | setting.onChange.apply($this, [ 796 | $bar, 797 | saveData.schedule[scKey] 798 | ]); 799 | } 800 | } 801 | } 802 | methods._resetBarPosition.apply($this, [timeline]); 803 | }); 804 | }, 805 | /** 806 | * initialize 807 | */ 808 | init: function (options) { 809 | return this.each(function () { 810 | let $this = $(this); 811 | let config = $.extend({ 812 | className: 'jq-schedule', 813 | rows: {}, 814 | startTime: '07:00', 815 | endTime: '19:30', 816 | widthTimeX: 25, // 1cell辺りの幅(px) 817 | widthTime: 600, // 区切り時間(秒) 818 | timeLineY: 50, // timeline height(px) 819 | timeLineBorder: 1, // timeline height border 820 | timeBorder: 1, // border width 821 | timeLinePaddingTop: 0, 822 | timeLinePaddingBottom: 0, 823 | headTimeBorder: 1, // time border width 824 | dataWidth: 160, // data width 825 | verticalScrollbar: 0, // vertical scrollbar width 826 | bundleMoveWidth: 1, // width to move all schedules to the right of the clicked time cell 827 | draggable: true, 828 | resizable: true, 829 | resizableLeft: false, 830 | // event 831 | onInitRow: null, 832 | onChange: null, 833 | onClick: null, 834 | onAppendRow: null, 835 | onAppendSchedule: null, 836 | onScheduleClick: null 837 | }, options); 838 | methods._saveSettingData.apply($this, [config]); 839 | 840 | let tableStartTime = methods.calcStringTime(config.startTime); 841 | let tableEndTime = methods.calcStringTime(config.endTime); 842 | tableStartTime -= (tableStartTime % config.widthTime); 843 | tableEndTime -= (tableEndTime % config.widthTime); 844 | methods._saveData.apply($this, [{ 845 | tableStartTime: tableStartTime, 846 | tableEndTime: tableEndTime 847 | }]); 848 | 849 | let html = '' + 850 | '
' + '\n' + 851 | '
 
' + '\n' + 852 | '
' + '\n' + 853 | '
' + '\n' + 854 | '
' + '\n' + 855 | '
' + '\n' + 856 | '
' + '\n' + 857 | '
' + '\n' + 858 | '
' + '\n' + 859 | '
' + '\n' + 860 | '
' + '\n' + 861 | '
' + '\n' + 862 | '
' + '\n' + 863 | '
' + '\n' + 864 | '
' + '\n' + 865 | '
'; 866 | 867 | $this.append(html); 868 | $this.addClass(config.className); 869 | 870 | $this.find('.sc_main_box').on('scroll', function () { 871 | $this.find('.sc_data_scroll').css('top', $(this).scrollTop() * -1); 872 | $this.find('.sc_header_scroll').css('left', $(this).scrollLeft() * -1); 873 | }); 874 | // add time cell 875 | // var cellNum = Math.floor((tableEndTime - tableStartTime) / config.widthTime); 876 | let beforeTime = -1; 877 | for (let t = tableStartTime; t < tableEndTime; t += config.widthTime) { 878 | if ( 879 | (beforeTime < 0) 880 | || (Math.floor(beforeTime / 3600) !== Math.floor(t / 3600))) { 881 | html = ''; 882 | html += '
' + methods.formatTime(t) + '
'; 883 | let $time = $(html); 884 | let cn = Number( 885 | Math.min((Math.ceil((t + config.widthTime) / 3600) * 3600), tableEndTime) - 886 | t 887 | ); 888 | let cellNum = Math.floor(cn / config.widthTime); 889 | $time.width((cellNum * config.widthTimeX)); 890 | $this.find('.sc_header_scroll').append($time); 891 | 892 | beforeTime = t; 893 | } 894 | } 895 | 896 | $(window).on('resize', function () { 897 | methods._resizeWindow.apply($this); 898 | }).trigger('resize'); 899 | 900 | // addrow 901 | for (let i in config.rows) { 902 | methods._addRow.apply($this, [i, config.rows[i]]); 903 | } 904 | }); 905 | } 906 | }; 907 | /** 908 | * 909 | * @param {Object|string} method 910 | * @returns {jQuery|methods|*} 911 | */ 912 | // eslint-disable-next-line no-param-reassign 913 | $.fn.timeSchedule = function (method) { 914 | // Method calling logic 915 | if (methods[method]) { 916 | return methods[method].apply(this, Array.prototype.slice.call(arguments, 1)); 917 | // eslint-disable-next-line no-else-return 918 | } else if (typeof method === 'object' || !method) { 919 | return methods.init.apply(this, arguments); 920 | } 921 | $.error('Method ' + method + ' does not exist on jQuery.timeSchedule'); 922 | return this; 923 | }; 924 | }(jQuery)); 925 | -------------------------------------------------------------------------------- /dist/js/jq.schedule.min.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"jq.schedule.min.js","sources":["js/jq.schedule.js"],"sourcesContent":["\"use strict\";\n\n(function ($) {\n 'use strict';\n\n const PLUGIN_NAME = 'jqSchedule';\n var methods = {\n /**\n *\n * @param {string} str\n * @returns {number}\n */\n calcStringTime: function (str) {\n let slice = str.split(':');\n let h = Number(slice[0]) * 60 * 60;\n let i = Number(slice[1]) * 60;\n return h + i;\n },\n /**\n *\n * @param {number} val\n * @returns {string}\n */\n formatTime: function (val) {\n let i1 = val % 3600;\n let h = '' + Math.floor(val / 36000) + Math.floor(val / 3600 % 10);\n let i = '' + Math.floor(i1 / 600) + Math.floor(i1 / 60 % 10);\n return h + ':' + i;\n },\n /**\n * 設定データの保存\n *\n * @param {Options} data\n * @returns {*}\n */\n _saveSettingData: function (data) {\n return this.data(PLUGIN_NAME + 'Setting', data);\n },\n /**\n * 設定データの取得\n *\n * @returns Options\n */\n _loadSettingData: function () {\n return this.data(PLUGIN_NAME + 'Setting');\n },\n /**\n * 保存データの保存\n *\n * @param {SaveData} data\n * @returns {*}\n */\n _saveData: function (data) {\n let d = $.extend({\n tableStartTime: 0,\n tableEndTime: 0,\n schedule: [],\n timeline: []\n }, data);\n return this.data(PLUGIN_NAME, d);\n },\n /**\n * 保存データの取得\n *\n * @returns SaveData\n */\n _loadData: function () {\n return this.data(PLUGIN_NAME);\n },\n /**\n * スケジュールの取得\n *\n * @returns ScheduleData[]\n */\n scheduleData: function () {\n let $this = $(this);\n let saveData = methods._loadData.apply($this);\n if (saveData) {\n return saveData.schedule;\n }\n return [];\n },\n /**\n * get timelineData\n * @returns {any[]}\n */\n timelineData: function () {\n let $this = $(this);\n let saveData = methods._loadData.apply($this);\n let data = [];\n let i;\n for (i in saveData.timeline) {\n data[i] = saveData.timeline[i];\n data[i].schedule = [];\n }\n for (i in saveData.schedule) {\n var d = saveData.schedule[i];\n if (typeof d.timeline === 'undefined') {\n continue;\n }\n if (typeof data[d.timeline] === 'undefined') {\n continue;\n }\n data[d.timeline].schedule.push(d);\n }\n return data;\n },\n /**\n * reset data\n */\n resetData: function () {\n return this.each(function () {\n let $this = $(this);\n let saveData = methods._loadData.apply($this);\n saveData.schedule = [];\n methods._saveData.apply($this, [saveData]);\n $this.find('.sc_bar').remove();\n for (var i in saveData.timeline) {\n saveData.timeline[i].schedule = [];\n methods._resizeRow.apply($this, [i, 0]);\n }\n methods._saveData.apply($this, [saveData]);\n });\n },\n /**\n * add schedule data\n *\n * @param {number} timeline\n * @param {object} data\n * @returns {methods}\n */\n addSchedule: function (timeline, data) {\n return this.each(function () {\n let $this = $(this);\n let d = {\n start: data.start,\n end: data.end,\n startTime: methods.calcStringTime(data.start),\n endTime: methods.calcStringTime(data.end),\n text: data.text,\n timeline: timeline\n };\n if (data.data) {\n d.data = data.data;\n }\n methods._addScheduleData.apply($this, [timeline, d]);\n methods._resetBarPosition.apply($this, [timeline]);\n });\n },\n /**\n * add schedule data\n *\n * @param {number} timeline\n * @param {object} data\n * @returns {methods}\n */\n addRow: function (timeline, data) {\n return this.each(function () {\n let $this = $(this);\n methods._addRow.apply($this, [timeline, data]);\n });\n },\n /**\n * clear row\n *\n * @returns {methods}\n */\n resetRowData: function () {\n return this.each(function () {\n let $this = $(this);\n let data = methods._loadData.apply($this);\n data.schedule = [];\n data.timeline = [];\n methods._saveData.apply($this, [data]);\n $this.find('.sc_bar').remove();\n $this.find('.timeline').remove();\n $this.find('.sc_data').height(0);\n });\n },\n /**\n * clear row\n *\n * @param {object} data\n * @returns {methods}\n */\n setRows: function (data) {\n return this.each(function () {\n let $this = $(this);\n methods.resetRowData.apply($this, []);\n for (var timeline in data) {\n methods.addRow.apply($this, [timeline, data[timeline]]);\n }\n });\n },\n /**\n * switch draggable\n * @param {boolean} enable\n */\n setDraggable: function (enable) {\n return this.each(function () {\n let $this = $(this);\n let setting = methods._loadSettingData.apply($this);\n if (enable !== setting.draggable) {\n setting.draggable = enable;\n methods._saveSettingData.apply($this, setting);\n if (enable) {\n $this.find('.sc_bar').draggable('enable');\n } else {\n $this.find('.sc_bar').draggable('disable');\n }\n }\n });\n },\n /**\n * switch resizable\n * @param {boolean} enable\n */\n setResizable: function (enable) {\n return this.each(function () {\n let $this = $(this);\n let setting = methods._loadSettingData.apply($this);\n if (enable !== setting.resizable) {\n setting.resizable = enable;\n methods._saveSettingData.apply($this, setting);\n if (enable) {\n $this.find('.sc_bar').resizable('enable');\n } else {\n $this.find('.sc_bar').resizable('disable');\n }\n }\n });\n },\n /**\n * 現在のタイムライン番号を取得\n *\n * @param node\n * @param top\n * @returns {number}\n */\n _getTimeLineNumber: function (node, top) {\n let $this = $(this);\n let setting = methods._loadSettingData.apply($this);\n let num = 0;\n let n = 0;\n let tn = Math.ceil(top / (setting.timeLineY + setting.timeLinePaddingTop + setting.timeLinePaddingBottom));\n for (var i in setting.rows) {\n let r = setting.rows[i];\n let tr = 0;\n if (typeof r.schedule === 'object') {\n tr = r.schedule.length;\n }\n if (node && node.timeline) {\n tr++;\n }\n n += Math.max(tr, 1);\n if (n >= tn) {\n break;\n }\n num++;\n }\n return num;\n },\n /**\n * 背景データ追加\n *\n * @param {ScheduleData} data\n */\n _addScheduleBgData: function (data) {\n return this.each(function () {\n let $this = $(this);\n let setting = methods._loadSettingData.apply($this);\n let saveData = methods._loadData.apply($this);\n let st = Math.ceil((data.startTime - saveData.tableStartTime) / setting.widthTime);\n let et = Math.floor((data.endTime - saveData.tableStartTime) / setting.widthTime);\n let $bar = $('
');\n $bar.css({\n left: st * setting.widthTimeX,\n top: 0,\n width: (et - st) * setting.widthTimeX,\n height: $this.find('.sc_main .timeline').eq(data.timeline).height()\n });\n if (data.text) {\n $bar.find('.text').text(data.text);\n }\n if (data.class) {\n $bar.addClass(data.class);\n }\n // $element.find('.sc_main').append($bar);\n $this.find('.sc_main .timeline').eq(data.timeline).append($bar);\n });\n },\n /**\n * スケジュール追加\n *\n * @param timeline\n * @param {ScheduleData} d\n * @returns {number}\n */\n _addScheduleData: function (timeline, d) {\n let data = d;\n data.startTime = data.startTime ? data.startTime : methods.calcStringTime(data.start);\n data.endTime = data.endTime ? data.endTime : methods.calcStringTime(data.end);\n return this.each(function () {\n let $this = $(this);\n let setting = methods._loadSettingData.apply($this);\n let saveData = methods._loadData.apply($this);\n let st = Math.ceil((data.startTime - saveData.tableStartTime) / setting.widthTime);\n let et = Math.floor((data.endTime - saveData.tableStartTime) / setting.widthTime);\n let $bar = $('
');\n let stext = methods.formatTime(data.startTime);\n let etext = methods.formatTime(data.endTime);\n let snum = methods._getScheduleCount.apply($this, [data.timeline]);\n $bar.css({\n left: st * setting.widthTimeX,\n top: snum * setting.timeLineY + setting.timeLinePaddingTop,\n width: (et - st) * setting.widthTimeX,\n height: setting.timeLineY\n });\n $bar.find('.time').text(stext + '-' + etext);\n if (data.text) {\n $bar.find('.text').text(data.text);\n }\n if (data.class) {\n $bar.addClass(data.class);\n }\n // $this.find('.sc_main').append($bar);\n var $row = $this.find('.sc_main .timeline').eq(timeline);\n $row.append($bar);\n // データの追加\n saveData.schedule.push(data);\n methods._saveData.apply($this, [saveData]);\n // コールバックがセットされていたら呼出\n if (setting.onAppendSchedule) {\n setting.onAppendSchedule.apply($this, [$bar, data]);\n }\n // key\n var key = saveData.schedule.length - 1;\n $bar.data('sc_key', key);\n $bar.on('mouseup', function () {\n // コールバックがセットされていたら呼出\n if (setting.onClick) {\n if ($(this).data('dragCheck') !== true && $(this).data('resizeCheck') !== true) {\n let $n = $(this);\n let scKey = $n.data('sc_key');\n setting.onClick.apply($this, [$n, saveData.schedule[scKey]]);\n }\n }\n });\n var $node = $this.find('.sc_bar');\n let currentNode = null;\n // move node.\n $node.draggable({\n grid: [setting.widthTimeX, 1],\n containment: $this.find('.sc_main'),\n helper: 'original',\n start: function (event, ui) {\n let node = {};\n node.node = this;\n node.offsetTop = ui.position.top;\n node.offsetLeft = ui.position.left;\n node.currentTop = ui.position.top;\n node.currentLeft = ui.position.left;\n node.timeline = methods._getTimeLineNumber.apply($this, [currentNode, ui.position.top]);\n node.nowTimeline = node.timeline;\n currentNode = node;\n },\n /**\n *\n * @param {Event} event\n * @param {function} ui\n * @returns {boolean}\n */\n drag: function (event, ui) {\n $(this).data('dragCheck', true);\n if (!currentNode) {\n return false;\n }\n let $moveNode = $(this);\n let scKey = $moveNode.data('sc_key');\n let timelineNum = methods._getTimeLineNumber.apply($this, [currentNode, ui.position.top]);\n // eslint-disable-next-line no-param-reassign\n ui.position.left = Math.floor(ui.position.left / setting.widthTimeX) * setting.widthTimeX;\n if (currentNode.nowTimeline !== timelineNum) {\n // 現在のタイムライン\n currentNode.nowTimeline = timelineNum;\n }\n currentNode.currentTop = ui.position.top;\n currentNode.currentLeft = ui.position.left;\n // テキスト変更\n methods._rewriteBarText.apply($this, [$moveNode, saveData.schedule[scKey]]);\n return true;\n },\n // 要素の移動が終った後の処理\n stop: function () {\n $(this).data('dragCheck', false);\n currentNode = null;\n let $n = $(this);\n let scKey = $n.data('sc_key');\n let x = $n.position().left;\n // var w = $n.width();\n let start = saveData.tableStartTime + Math.floor(x / setting.widthTimeX) * setting.widthTime;\n // var end = saveData.tableStartTime + (Math.floor((x + w) / setting.widthTimeX) * setting.widthTime);\n let end = start + (saveData.schedule[scKey].endTime - saveData.schedule[scKey].startTime);\n saveData.schedule[scKey].start = methods.formatTime(start);\n saveData.schedule[scKey].end = methods.formatTime(end);\n saveData.schedule[scKey].startTime = start;\n saveData.schedule[scKey].endTime = end;\n // コールバックがセットされていたら呼出\n if (setting.onChange) {\n setting.onChange.apply($this, [$n, saveData.schedule[scKey]]);\n }\n }\n });\n let resizableHandles = ['e'];\n if (setting.resizableLeft) {\n resizableHandles.push('w');\n }\n $node.resizable({\n handles: resizableHandles.join(','),\n grid: [setting.widthTimeX, setting.timeLineY - setting.timeBorder],\n minWidth: setting.widthTimeX,\n containment: $this.find('.sc_main_scroll'),\n start: function () {\n let $n = $(this);\n $n.data('resizeCheck', true);\n },\n resize: function (ev, ui) {\n // box-sizing: border-box; に対応\n ui.element.height(ui.size.height);\n ui.element.width(ui.size.width);\n },\n // 要素の移動が終った後の処理\n stop: function () {\n let $n = $(this);\n let scKey = $n.data('sc_key');\n let x = $n.position().left;\n let w = $n.outerWidth();\n let start = saveData.tableStartTime + Math.floor(x / setting.widthTimeX) * setting.widthTime;\n let end = saveData.tableStartTime + Math.floor((x + w) / setting.widthTimeX) * setting.widthTime;\n let timelineNum = saveData.schedule[scKey].timeline;\n saveData.schedule[scKey].start = methods.formatTime(start);\n saveData.schedule[scKey].end = methods.formatTime(end);\n saveData.schedule[scKey].startTime = start;\n saveData.schedule[scKey].endTime = end;\n\n // 高さ調整\n methods._resetBarPosition.apply($this, [timelineNum]);\n // テキスト変更\n methods._rewriteBarText.apply($this, [$n, saveData.schedule[scKey]]);\n $n.data('resizeCheck', false);\n // コールバックがセットされていたら呼出\n if (setting.onChange) {\n setting.onChange.apply($this, [$n, saveData.schedule[scKey]]);\n }\n }\n });\n if (setting.draggable === false) {\n $node.draggable('disable');\n }\n if (setting.resizable === false) {\n $node.resizable('disable');\n }\n return key;\n });\n },\n /**\n * スケジュール数の取得\n *\n * @param {number} n row number\n * @returns {number}\n */\n _getScheduleCount: function (n) {\n let $this = $(this);\n let saveData = methods._loadData.apply($this);\n let num = 0;\n for (let i in saveData.schedule) {\n if (saveData.schedule[i].timeline === n) {\n num++;\n }\n }\n return num;\n },\n /**\n * add rows\n *\n * @param timeline\n * @param row\n */\n _addRow: function (timeline, row) {\n return this.each(function () {\n let $this = $(this);\n let setting = methods._loadSettingData.apply($this);\n let saveData = methods._loadData.apply($this);\n let id = $this.find('.sc_main .timeline').length;\n let html;\n html = '';\n html += '
';\n let $data = $(html);\n if (row.title) {\n $data.append('' + row.title + '');\n }\n if (row.subtitle) {\n $data.append('' + row.subtitle + '');\n }\n // event call\n if (setting.onInitRow) {\n setting.onInitRow.apply($this, [$data, row]);\n }\n $this.find('.sc_data_scroll').append($data);\n html = '';\n html += '
';\n let $timeline = $(html);\n for (var t = saveData.tableStartTime; t < saveData.tableEndTime; t += setting.widthTime) {\n var $tl = $('
');\n $tl.outerWidth(setting.widthTimeX);\n $tl.data('time', methods.formatTime(t));\n $tl.data('timeline', timeline);\n $timeline.append($tl);\n }\n // クリックイベント\n // left click\n $timeline.find('.tl').on('click', function () {\n if (setting.onScheduleClick) {\n setting.onScheduleClick.apply($this, [this, $(this).data('time'), $(this).data('timeline'), saveData.timeline[$(this).data('timeline')]]);\n }\n });\n // right click\n $timeline.find('.tl').on('contextmenu', function () {\n if (setting.onScheduleClick) {\n setting.onScheduleClick.apply($this, [this, $(this).data('time'), $(this).data('timeline'), saveData.timeline[$(this).data('timeline')]]);\n }\n return false;\n });\n $this.find('.sc_main').append($timeline);\n saveData.timeline[timeline] = row;\n methods._saveData.apply($this, [saveData]);\n if (row.class && row.class !== '') {\n $this.find('.sc_data .timeline').eq(id).addClass(row.class);\n $this.find('.sc_main .timeline').eq(id).addClass(row.class);\n }\n // スケジュールタイムライン\n if (row.schedule) {\n for (var i in row.schedule) {\n var bdata = row.schedule[i];\n var s = bdata.start ? bdata.start : methods.calcStringTime(bdata.startTime);\n var e = bdata.end ? bdata.end : methods.calcStringTime(bdata.endTime);\n var data = {};\n data.start = s;\n data.end = e;\n if (bdata.text) {\n data.text = bdata.text;\n }\n data.timeline = timeline;\n data.data = {};\n if (bdata.data) {\n data.data = bdata.data;\n }\n methods._addScheduleData.apply($this, [id, data]);\n }\n }\n // 高さの調整\n methods._resetBarPosition.apply($this, [id]);\n $this.find('.sc_main .timeline').eq(id).droppable({\n accept: '.sc_bar',\n drop: function (ev, ui) {\n let node = ui.draggable;\n let scKey = node.data('sc_key');\n let nowTimelineNum = saveData.schedule[scKey].timeline;\n let timelineNum = $this.find('.sc_main .timeline').index(this);\n // タイムラインの変更\n saveData.schedule[scKey].timeline = timelineNum;\n node.appendTo(this);\n // 高さ調整\n methods._resetBarPosition.apply($this, [nowTimelineNum]);\n methods._resetBarPosition.apply($this, [timelineNum]);\n }\n });\n // コールバックがセットされていたら呼出\n if (setting.onAppendRow) {\n $this.find('.sc_main .timeline').eq(id).find('.sc_bar').each(function () {\n let $n = $(this);\n let scKey = $n.data('sc_key');\n setting.onAppendRow.apply($this, [$n, saveData.schedule[scKey]]);\n });\n }\n });\n },\n /**\n * テキストの変更\n *\n * @param {jQuery} node\n * @param {Object} data\n */\n _rewriteBarText: function (node, data) {\n return this.each(function () {\n let $this = $(this);\n let setting = methods._loadSettingData.apply($this);\n let saveData = methods._loadData.apply($this);\n let x = node.position().left;\n // var w = node.width();\n let start = saveData.tableStartTime + Math.floor(x / setting.widthTimeX) * setting.widthTime;\n // var end = saveData.tableStartTime + (Math.floor((x + w) / setting.widthTimeX) * setting.widthTime);\n let end = start + (data.endTime - data.startTime);\n let html = methods.formatTime(start) + '-' + methods.formatTime(end);\n $(node).find('.time').html(html);\n });\n },\n /**\n *\n * @param {Number} n\n */\n _resetBarPosition: function (n) {\n return this.each(function () {\n let $this = $(this);\n let setting = methods._loadSettingData.apply($this);\n // 要素の並び替え\n let $barList = $this.find('.sc_main .timeline').eq(n).find('.sc_bar');\n let codes = [],\n check = [];\n let h = 0;\n let $e1, $e2;\n let c1, c2, s1, s2, e1, e2;\n let i;\n for (i = 0; i < $barList.length; i++) {\n codes[i] = {\n code: i,\n x: $($barList[i]).position().left\n };\n }\n // ソート\n codes.sort(function (a, b) {\n if (a.x < b.x) {\n return -1;\n }\n if (a.x > b.x) {\n return 1;\n }\n return 0;\n });\n for (i = 0; i < codes.length; i++) {\n c1 = codes[i].code;\n $e1 = $($barList[c1]);\n for (h = 0; h < check.length; h++) {\n let next = false;\n for (var j = 0; j < check[h].length; j++) {\n c2 = check[h][j];\n $e2 = $($barList[c2]);\n s1 = $e1.position().left;\n e1 = $e1.position().left + $e1.outerWidth();\n s2 = $e2.position().left;\n e2 = $e2.position().left + $e2.outerWidth();\n if (s1 < e2 && e1 > s2) {\n next = true;\n continue;\n }\n }\n if (!next) {\n break;\n }\n }\n if (!check[h]) {\n check[h] = [];\n }\n $e1.css({\n top: h * setting.timeLineY + setting.timeLinePaddingTop\n });\n check[h][check[h].length] = c1;\n }\n // 高さの調整\n methods._resizeRow.apply($this, [n, check.length]);\n });\n },\n /**\n *\n * @param n\n * @param height\n */\n _resizeRow: function (n, height) {\n return this.each(function () {\n let $this = $(this);\n let setting = methods._loadSettingData.apply($this);\n let h = Math.max(height, 1);\n $this.find('.sc_data .timeline').eq(n).outerHeight(h * setting.timeLineY + setting.timeLineBorder + setting.timeLinePaddingTop + setting.timeLinePaddingBottom);\n $this.find('.sc_main .timeline').eq(n).outerHeight(h * setting.timeLineY + setting.timeLineBorder + setting.timeLinePaddingTop + setting.timeLinePaddingBottom);\n $this.find('.sc_main .timeline').eq(n).find('.sc_bgBar').each(function () {\n $(this).outerHeight($(this).closest('.timeline').outerHeight());\n });\n $this.find('.sc_data').outerHeight($this.find('.sc_main_box').outerHeight());\n });\n },\n /**\n * resizeWindow\n */\n _resizeWindow: function () {\n return this.each(function () {\n let $this = $(this);\n let setting = methods._loadSettingData.apply($this);\n let saveData = methods._loadData.apply($this);\n let scWidth = $this.width();\n let scMainWidth = scWidth - setting.dataWidth - setting.verticalScrollbar;\n let cellNum = Math.floor((saveData.tableEndTime - saveData.tableStartTime) / setting.widthTime);\n $this.find('.sc_header_cell').width(setting.dataWidth);\n $this.find('.sc_data,.sc_data_scroll').width(setting.dataWidth);\n $this.find('.sc_header').width(scMainWidth);\n $this.find('.sc_main_box').width(scMainWidth);\n $this.find('.sc_header_scroll').width(setting.widthTimeX * cellNum);\n $this.find('.sc_main_scroll').width(setting.widthTimeX * cellNum);\n });\n },\n /**\n * move all cells of the right of the specified time line cell\n *\n * @param timeline\n * @param baseTimeLineCell\n * @param moveWidth\n */\n _moveSchedules: function (timeline, baseTimeLineCell, moveWidth) {\n return this.each(function () {\n let $this = $(this);\n let setting = methods._loadSettingData.apply($this);\n let saveData = methods._loadData.apply($this);\n let $barList = $this.find('.sc_main .timeline').eq(timeline).find('.sc_bar');\n for (let i = 0; i < $barList.length; i++) {\n let $bar = $($barList[i]);\n if (baseTimeLineCell.position().left <= $bar.position().left) {\n let v1 = $bar.position().left + setting.widthTimeX * moveWidth;\n let v2 = Math.floor((saveData.tableEndTime - saveData.tableStartTime) / setting.widthTime) * setting.widthTimeX - $bar.outerWidth();\n $bar.css({\n left: Math.max(0, Math.min(v1, v2))\n });\n let scKey = $bar.data('sc_key');\n let start = saveData.tableStartTime + Math.floor($bar.position().left / setting.widthTimeX) * setting.widthTime;\n let end = start + (saveData.schedule[scKey].end - saveData.schedule[scKey].start);\n saveData.schedule[scKey].start = methods.formatTime(start);\n saveData.schedule[scKey].end = methods.formatTime(end);\n saveData.schedule[scKey].startTime = start;\n saveData.schedule[scKey].endTime = end;\n methods._rewriteBarText.apply($this, [$bar, saveData.schedule[scKey]]);\n\n // if setting\n if (setting.onChange) {\n setting.onChange.apply($this, [$bar, saveData.schedule[scKey]]);\n }\n }\n }\n methods._resetBarPosition.apply($this, [timeline]);\n });\n },\n /**\n * initialize\n */\n init: function (options) {\n return this.each(function () {\n let $this = $(this);\n let config = $.extend({\n className: 'jq-schedule',\n rows: {},\n startTime: '07:00',\n endTime: '19:30',\n widthTimeX: 25,\n // 1cell辺りの幅(px)\n widthTime: 600,\n // 区切り時間(秒)\n timeLineY: 50,\n // timeline height(px)\n timeLineBorder: 1,\n // timeline height border\n timeBorder: 1,\n // border width\n timeLinePaddingTop: 0,\n timeLinePaddingBottom: 0,\n headTimeBorder: 1,\n // time border width\n dataWidth: 160,\n // data width\n verticalScrollbar: 0,\n // vertical scrollbar width\n bundleMoveWidth: 1,\n // width to move all schedules to the right of the clicked time cell\n draggable: true,\n resizable: true,\n resizableLeft: false,\n // event\n onInitRow: null,\n onChange: null,\n onClick: null,\n onAppendRow: null,\n onAppendSchedule: null,\n onScheduleClick: null\n }, options);\n methods._saveSettingData.apply($this, [config]);\n let tableStartTime = methods.calcStringTime(config.startTime);\n let tableEndTime = methods.calcStringTime(config.endTime);\n tableStartTime -= tableStartTime % config.widthTime;\n tableEndTime -= tableEndTime % config.widthTime;\n methods._saveData.apply($this, [{\n tableStartTime: tableStartTime,\n tableEndTime: tableEndTime\n }]);\n let html = '' + '
' + '\\n' + '
 
' + '\\n' + '
' + '\\n' + '
' + '\\n' + '
' + '\\n' + '
' + '\\n' + '
' + '\\n' + '
' + '\\n' + '
' + '\\n' + '
' + '\\n' + '
' + '\\n' + '
' + '\\n' + '
' + '\\n' + '
' + '\\n' + '
' + '\\n' + '
';\n $this.append(html);\n $this.addClass(config.className);\n $this.find('.sc_main_box').on('scroll', function () {\n $this.find('.sc_data_scroll').css('top', $(this).scrollTop() * -1);\n $this.find('.sc_header_scroll').css('left', $(this).scrollLeft() * -1);\n });\n // add time cell\n // var cellNum = Math.floor((tableEndTime - tableStartTime) / config.widthTime);\n let beforeTime = -1;\n for (let t = tableStartTime; t < tableEndTime; t += config.widthTime) {\n if (beforeTime < 0 || Math.floor(beforeTime / 3600) !== Math.floor(t / 3600)) {\n html = '';\n html += '
' + methods.formatTime(t) + '
';\n let $time = $(html);\n let cn = Number(Math.min(Math.ceil((t + config.widthTime) / 3600) * 3600, tableEndTime) - t);\n let cellNum = Math.floor(cn / config.widthTime);\n $time.width(cellNum * config.widthTimeX);\n $this.find('.sc_header_scroll').append($time);\n beforeTime = t;\n }\n }\n $(window).on('resize', function () {\n methods._resizeWindow.apply($this);\n }).trigger('resize');\n\n // addrow\n for (let i in config.rows) {\n methods._addRow.apply($this, [i, config.rows[i]]);\n }\n });\n }\n };\n /**\n *\n * @param {Object|string} method\n * @returns {jQuery|methods|*}\n */\n // eslint-disable-next-line no-param-reassign\n $.fn.timeSchedule = function (method) {\n // Method calling logic\n if (methods[method]) {\n return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));\n // eslint-disable-next-line no-else-return\n } else if (typeof method === 'object' || !method) {\n return methods.init.apply(this, arguments);\n }\n $.error('Method ' + method + ' does not exist on jQuery.timeSchedule');\n return this;\n };\n})(jQuery);"],"names":["$","PLUGIN_NAME","methods","calcStringTime","str","slice","split","Number","formatTime","val","let","i1","Math","floor","_saveSettingData","data","this","_loadSettingData","_saveData","d","extend","tableStartTime","tableEndTime","schedule","timeline","_loadData","scheduleData","$this","saveData","apply","timelineData","i","push","resetData","each","find","remove","_resizeRow","addSchedule","start","end","startTime","endTime","text","_addScheduleData","_resetBarPosition","addRow","_addRow","resetRowData","height","setRows","setDraggable","enable","setting","draggable","setResizable","resizable","_getTimeLineNumber","node","top","num","n","tn","ceil","timeLineY","timeLinePaddingTop","timeLinePaddingBottom","rows","r","tr","length","max","_addScheduleBgData","st","widthTime","et","$bar","css","left","widthTimeX","width","eq","class","addClass","append","stext","etext","snum","_getScheduleCount","onAppendSchedule","key","on","onClick","$n","scKey","$node","currentNode","resizableHandles","grid","containment","helper","event","ui","offsetTop","position","offsetLeft","currentTop","currentLeft","nowTimeline","drag","$moveNode","timelineNum","_rewriteBarText","stop","x","onChange","resizableLeft","handles","join","timeBorder","minWidth","resize","ev","element","size","w","outerWidth","row","id","html","$data","$timeline","title","subtitle","onInitRow","t","$tl","onScheduleClick","bdata","s","e","droppable","accept","drop","nowTimelineNum","index","appendTo","onAppendRow","c1","s1","s2","e1","$barList","codes","check","h","$e1","$e2","code","sort","a","b","next","j","c2","outerHeight","timeLineBorder","closest","_resizeWindow","scMainWidth","dataWidth","verticalScrollbar","cellNum","_moveSchedules","baseTimeLineCell","moveWidth","v1","v2","min","init","options","config","className","headTimeBorder","bundleMoveWidth","scrollTop","scrollLeft","beforeTime","$time","cn","window","trigger","fn","timeSchedule","method","Array","prototype","call","arguments","error","jQuery"],"mappings":"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"} --------------------------------------------------------------------------------