<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>jq.Schedule Demo</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" type="text/css" href="https://code.jquery.com/ui/1.9.2/themes/base/jquery-ui.css" />
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<style>
.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-x:auto;overflow-y: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:#2e7ac4}
.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}
body {
padding-top: 30px; /* 60px to make the container go all the way to the bottom of the topbar */
}
#logs{
border: solid 1px #bbb;
padding: 16px;
background: #eee;
}
#logs .table{
margin-bottom: 0;
}
#logs .table td,
#logs .table th{
border: none;
}
#schedule .sc_bar_insert{
background-color: #ff678a;
}
#schedule .example2{
background-color: #3eb698;
}
#schedule .example3{
color: #2c0000;
font-weight: bold;
background-color: #c7ae50;
}
#schedule .sc_bar.sc_bar_photo .head,
#schedule .sc_bar.sc_bar_photo .text{
padding-left: 60px;
}
#schedule .sc_bar.sc_bar_photo .photo{
position: absolute;
left: 10px;
top: 10px;
width: 38px;
}
#schedule .sc_bar.sc_bar_photo .photo img{
max-width: 100%;
}
</style>
</head>
<body>
<h1><a href="https://plnkr.co/plunk/FUeFIbTJsgSjAwEl" target="thebest">latest the best version is here</a></h1>
<div class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">jQ.Scuedule.js</a>
</div>
<div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li class="active"><a href="#home">Home</a></li>
<li><a href="#demo">Demo</a></li>
<li><a href="#info">Get Start</a></li>
<li><a href="#option">Option</a></li>
</ul>
</div>
</div>
</div>
<div class="container">
<div class="jumbotron" style="padding-bottom: 16px; padding-top: 24px;">
<h1 id="home" class="h2" style="padding-bottom: 16px"><a href=" https://plnkr.co/plunk/9tSKRGqNFMgrgBrH" target="mytimebar">Searching for a wifi switch scheduler that works on PC Phone and Tablet https://plnkr.co/plunk/9tSKRGqNFMgrgBrH</a><br>
jq.Schedule</h1>
<p>javascriptbase time schedule plugin.</p>
<p>
Drag and Drop Support/Resize Schedule/Ajax Support(Callback Event Option)
</p>
<div style="padding-bottom: 16px"><button onclick="javascript:location.href='https://github.com/ateliee/jquery.schedule';" class="btn btn-lg btn-primary" type="button">Download jq.Schedule</button></div>
<div>
<iframe src="https://ghbtns.com/github-btn.html?user=ateliee&repo=jquery.schedule&type=star&count=true&size=large" frameborder="0" scrolling="0" width="140px" height="30px"></iframe>
<iframe src="https://ghbtns.com/github-btn.html?user=ateliee&repo=jquery.schedule&type=watch&count=true&size=large&v=2" frameborder="0" scrolling="0" width="140px" height="30px"></iframe>
</div>
</div>
<h2 id="demo">Demo</h2>
<p>
Example Sample Demo
</p>
<h3>Method</h3>
<div style="padding: 0 0 12px;">
<button id="event_timelineData" class="btn btn-info" style="margin-bottom: 12px;">timelineData()</button>
<button id="event_scheduleData" class="btn btn-info" style="margin-bottom: 12px;">scheduleData()</button>
<button id="event_setDraggable" class="btn btn-info" style="margin-bottom: 12px;">toggleDraggable</button>
<button id="event_setResizable" class="btn btn-info" style="margin-bottom: 12px;">toggleResizable</button>
<button id="event_resetData" class="btn btn-danger" style="margin-bottom: 12px;">resetData()</button>
<button id="event_resetRowData" class="btn btn-danger" style="margin-bottom: 12px;">resetRowData()</button>
</div>
<h3>Ajax</h3>
<div style="padding: 0 0 12px;">
<button class="btn btn-info ajax-data" data-target="1.json" style="margin-bottom: 12px;">ajax1()</button>
<button class="btn btn-info ajax-data" data-target="2.json" style="margin-bottom: 12px;">ajax2()</button>
<button class="btn btn-info ajax-data" data-target="3.json" style="margin-bottom: 12px;">ajax3()</button>
</div>
<div style="padding: 0 0 40px;">
<div id="schedule"></div>
<div class="row">
<div class="col-md-8">
<h3>Log</h3>
</div>
<div class="col-md-4 text-right">
<a class="btn btn-default" style="margin-top: 16px;" id="clear-logs">clear</a>
</div>
</div>
<div style="padding: 12px 0 0;">
<div id="logs" class="table-responsive"></div>
</div>
</div>
<h2 id="info">Get Start</h2>
<ol>
<li><p>Please Download <a href="https://github.com/ateliee/jquery.schedule/archive/master.zip">jQuery.Schedule</a></p></li>
<li>
<p>include <code>jquery.js</code>and<code>jquery.ui.js</code> after jq.schedule</p>
<pre><code><script src="jquery-3.4.1.js" type="text/javascript" language="javascript"></script>
<script src="jquery-ui-2.3.2.js" type="text/javascript" language="javascript"></script>
<script type="text/javascript" src="../dist/js/jq.schedule.js"></script>
<link rel="stylesheet" type="text/css" href="../dist/css/style.min.css" /></code></pre>
</li>
<li>
<p>init scripts</p>
<pre><code><script type="text/javascript">
$(function(){
var $sc = jQuery("#schedule").timeSchedule($options);
});
</script></code></pre>
</li>
</ol>
<h2 id="option">Option</h2>
<div class="p">
<table class="table table-striped">
<tr>
<th colspan="2">name</th>
<th width="80">Type</th>
<th>description</th>
</tr>
<tr>
<td colspan="2">className</td>
<td>String</td>
<td>add elemnt class(default jq.schedule)</td>
</tr>
<tr>
<td colspan="2">startTime</td>
<td>String(HH:ii)</td>
<td>schedule start time</td>
</tr>
<tr>
<td colspan="2">endTime</td>
<td>String(HH:ii)</td>
<td>schedule end time</td>
</tr>
<tr>
<td colspan="2">widthTime</td>
<td>Integer</td>
<td>minuite time line cell</td>
</tr>
<tr>
<td colspan="2">timeLineY</td>
<td>Integer</td>
<td>cell height px</td>
</tr>
<tr>
<td colspan="2">verticalScrollbar</td>
<td>Integer</td>
<td>scrollbar (px)</td>
</tr>
<tr>
<td colspan="2">timeLineBorder</td>
<td>Integer</td>
<td>border(top and bottom)</td>
</tr>
<tr>
<td colspan="2">bundleMoveWidth</td>
<td>Integer</td>
<td>width to move all schedules to the right of the clicked time line cell.
</td>
</tr>
<tr>
<td colspan="2">draggable</td>
<td>Boolean</td>
<td>enable draggable</td>
</tr>
<tr>
<td colspan="2">resizable</td>
<td>Boolean</td>
<td>enable resizable</td>
</tr>
<tr>
<td colspan="2">resizableLeft</td>
<td>Boolean</td>
<td>enable left handle resizable(default false)</td>
</tr>
<tr>
<td rowspan="3">rows</td>
<td></td>
<td>Array</td>
<td>Schedule Data</td>
</tr>
<tr>
<td>title</td>
<td>String</td>
<td>Schedule Left Cell Text</td>
</tr>
<tr>
<td>subtitle</td>
<td>String(Option)</td>
<td>Schedule Left Cell Sub Text</td>
</tr>
<tr>
<td>schedule</td>
<td>Array</td>
<td>Schedule Cell Data</td>
</tr>
<tr>
<td colspan="2">onInitRow</td>
<td>function</td>
<td>callback init time</td>
</tr>
<tr>
<td colspan="2">onChange</td>
<td>function</td>
<td>callback change time</td>
</tr>
<tr>
<td colspan="2">onClick</td>
<td>function</td>
<td>callback click time</td>
</tr>
<tr>
<td colspan="2">onScheduleClick</td>
<td>function</td>
<td>callback timebar click</td>
</tr>
<tr>
<td colspan="2">onAppendRow</td>
<td>function</td>
<td>callback append time</td>
</tr>
<tr>
<td colspan="2">onAppendSchedule</td>
<td>function</td>
<td>callback append time data</td>
</tr>
</table>
</div>
<h2 id="data">Schedule Data</h2>
<div>
<table class="table table-striped">
<tr>
<th colspan="2">name</th>
<th width="80">Type</th>
<th>description</th>
</tr>
<tr>
<td colspan="2">title</td>
<td>String</td>
<td>Visible Text Schedule Bar</td>
</tr>
<tr>
<td colspan="2">class</td>
<td>String</td>
<td>add class name</td>
</tr>
<tr>
<td rowspan="5">schedule</td>
<td colspan="2">array</td>
<td>add schedule data</td>
</tr>
<tr>
<td>start</td>
<td>String</td>
<td>time String(HH:ii)</td>
</tr>
<tr>
<td>end</td>
<td>String</td>
<td>time String(HH:ii)</td>
</tr>
<tr>
<td>text</td>
<td>String</td>
<td>Show Text</td>
</tr>
<tr>
<td>data</td>
<td>Object,Array</td>
<td>callback data Object</td>
</tr>
</table>
</div>
</div>
<div id="footer">
<div class="container">
<p class="muted credit">Copyright © 2014 <a href="https://github.com/ateliee" target="_blank">ateliee</a> inc. All Rights Reserved.</p>
</div>
</div>
<script
src="https://code.jquery.com/jquery-3.4.1.min.js"
integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo="
crossorigin="anonymous"></script>
<script src="https://code.jquery.com/ui/1.10.4/jquery-ui.min.js" type="text/javascript" language="javascript"></script>
<!-- Latest compiled and minified JavaScript -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
<script >
"use strict";
function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
(function ($) {
'use strict';
var PLUGIN_NAME = 'jqSchedule';
var methods = {
/**
*
* @param {string} str
* @returns {number}
*/
calcStringTime: function calcStringTime(str) {
var slice = str.split(':');
var h = Number(slice[0]) * 60 * 60;
var i = Number(slice[1]) * 60;
return h + i;
},
/**
*
* @param {number} val
* @returns {string}
*/
formatTime: function formatTime(val) {
var i1 = val % 3600;
var h = '' + Math.floor(val / 36000) + Math.floor(val / 3600 % 10);
var i = '' + Math.floor(i1 / 600) + Math.floor(i1 / 60 % 10);
return h + ':' + i;
},
/**
* 設定データの保存
*
* @param {Options} data
* @returns {*}
*/
_saveSettingData: function _saveSettingData(data) {
return this.data(PLUGIN_NAME + 'Setting', data);
},
/**
* 設定データの取得
*
* @returns Options
*/
_loadSettingData: function _loadSettingData() {
return this.data(PLUGIN_NAME + 'Setting');
},
/**
* 保存データの保存
*
* @param {SaveData} data
* @returns {*}
*/
_saveData: function _saveData(data) {
var d = $.extend({
tableStartTime: 0,
tableEndTime: 0,
schedule: [],
timeline: []
}, data);
return this.data(PLUGIN_NAME, d);
},
/**
* 保存データの取得
*
* @returns SaveData
*/
_loadData: function _loadData() {
return this.data(PLUGIN_NAME);
},
/**
* スケジュールの取得
*
* @returns ScheduleData[]
*/
scheduleData: function scheduleData() {
var $this = $(this);
var saveData = methods._loadData.apply($this);
if (saveData) {
return saveData.schedule;
}
return [];
},
/**
* get timelineData
* @returns {any[]}
*/
timelineData: function timelineData() {
var $this = $(this);
var saveData = methods._loadData.apply($this);
var data = [];
var i;
for (i in saveData.timeline) {
data[i] = saveData.timeline[i];
data[i].schedule = [];
}
for (i in saveData.schedule) {
var d = saveData.schedule[i];
if (typeof d.timeline === 'undefined') {
continue;
}
if (typeof data[d.timeline] === 'undefined') {
continue;
}
data[d.timeline].schedule.push(d);
}
return data;
},
/**
* reset data
*/
resetData: function resetData() {
return this.each(function () {
var $this = $(this);
var saveData = methods._loadData.apply($this);
saveData.schedule = [];
methods._saveData.apply($this, [saveData]);
$this.find('.sc_bar').remove();
for (var i in saveData.timeline) {
saveData.timeline[i].schedule = [];
methods._resizeRow.apply($this, [i, 0]);
}
methods._saveData.apply($this, [saveData]);
});
},
/**
* add schedule data
*
* @param {number} timeline
* @param {object} data
* @returns {methods}
*/
addSchedule: function addSchedule(timeline, data) {
return this.each(function () {
var $this = $(this);
var d = {
start: data.start,
end: data.end,
startTime: methods.calcStringTime(data.start),
endTime: methods.calcStringTime(data.end),
text: data.text,
timeline: timeline
};
if (data.data) {
d.data = data.data;
}
methods._addScheduleData.apply($this, [timeline, d]);
methods._resetBarPosition.apply($this, [timeline]);
});
},
/**
* add schedule data
*
* @param {number} timeline
* @param {object} data
* @returns {methods}
*/
addRow: function addRow(timeline, data) {
return this.each(function () {
var $this = $(this);
methods._addRow.apply($this, [timeline, data]);
});
},
/**
* clear row
*
* @returns {methods}
*/
resetRowData: function resetRowData() {
return this.each(function () {
var $this = $(this);
var data = methods._loadData.apply($this);
data.schedule = [];
data.timeline = [];
methods._saveData.apply($this, [data]);
$this.find('.sc_bar').remove();
$this.find('.timeline').remove();
$this.find('.sc_data').height(0);
});
},
/**
* clear row
*
* @param {object} data
* @returns {methods}
*/
setRows: function setRows(data) {
return this.each(function () {
var $this = $(this);
methods.resetRowData.apply($this, []);
for (var timeline in data) {
methods.addRow.apply($this, [timeline, data[timeline]]);
}
});
},
/**
* switch draggable
* @param {boolean} enable
*/
setDraggable: function setDraggable(enable) {
return this.each(function () {
var $this = $(this);
var setting = methods._loadSettingData.apply($this);
if (enable !== setting.draggable) {
setting.draggable = enable;
methods._saveSettingData.apply($this, setting);
if (enable) {
$this.find('.sc_bar').draggable('enable');
} else {
$this.find('.sc_bar').draggable('disable');
}
}
});
},
/**
* switch resizable
* @param {boolean} enable
*/
setResizable: function setResizable(enable) {
return this.each(function () {
var $this = $(this);
var setting = methods._loadSettingData.apply($this);
if (enable !== setting.resizable) {
setting.resizable = enable;
methods._saveSettingData.apply($this, setting);
if (enable) {
$this.find('.sc_bar').resizable('enable');
} else {
$this.find('.sc_bar').resizable('disable');
}
}
});
},
/**
* 現在のタイムライン番号を取得
*
* @param node
* @param top
* @returns {number}
*/
_getTimeLineNumber: function _getTimeLineNumber(node, top) {
var $this = $(this);
var setting = methods._loadSettingData.apply($this);
var num = 0;
var n = 0;
var tn = Math.ceil(top / (setting.timeLineY + setting.timeLinePaddingTop + setting.timeLinePaddingBottom));
for (var i in setting.rows) {
var r = setting.rows[i];
var tr = 0;
if (_typeof(r.schedule) === 'object') {
tr = r.schedule.length;
}
if (node && node.timeline) {
tr++;
}
n += Math.max(tr, 1);
if (n >= tn) {
break;
}
num++;
}
return num;
},
/**
* 背景データ追加
*
* @param {ScheduleData} data
*/
_addScheduleBgData: function _addScheduleBgData(data) {
return this.each(function () {
var $this = $(this);
var setting = methods._loadSettingData.apply($this);
var saveData = methods._loadData.apply($this);
var st = Math.ceil((data.startTime - saveData.tableStartTime) / setting.widthTime);
var et = Math.floor((data.endTime - saveData.tableStartTime) / setting.widthTime);
var $bar = $('<div class="sc_bgBar"><span class="text"></span></div>');
$bar.css({
left: st * setting.widthTimeX,
top: 0,
width: (et - st) * setting.widthTimeX,
height: $this.find('.sc_main .timeline').eq(data.timeline).height()
});
if (data.text) {
$bar.find('.text').text(data.text);
}
if (data.class) {
$bar.addClass(data.class);
} // $element.find('.sc_main').append($bar);
$this.find('.sc_main .timeline').eq(data.timeline).append($bar);
});
},
/**
* スケジュール追加
*
* @param timeline
* @param {ScheduleData} d
* @returns {number}
*/
_addScheduleData: function _addScheduleData(timeline, d) {
var data = d;
data.startTime = data.startTime ? data.startTime : methods.calcStringTime(data.start);
data.endTime = data.endTime ? data.endTime : methods.calcStringTime(data.end);
return this.each(function () {
var $this = $(this);
var setting = methods._loadSettingData.apply($this);
var saveData = methods._loadData.apply($this);
var st = Math.ceil((data.startTime - saveData.tableStartTime) / setting.widthTime);
var et = Math.floor((data.endTime - saveData.tableStartTime) / setting.widthTime);
var $bar = $('<div class="sc_bar"><span class="head"><span class="time"></span></span><span class="text"></span></div>');
var stext = methods.formatTime(data.startTime);
var etext = methods.formatTime(data.endTime);
var snum = methods._getScheduleCount.apply($this, [data.timeline]);
$bar.css({
left: st * setting.widthTimeX,
top: snum * setting.timeLineY + setting.timeLinePaddingTop,
width: (et - st) * setting.widthTimeX,
height: setting.timeLineY
});
$bar.find('.time').text(stext + '-' + etext);
if (data.text) {
$bar.find('.text').text(data.text);
}
if (data.class) {
$bar.addClass(data.class);
} // $this.find('.sc_main').append($bar);
var $row = $this.find('.sc_main .timeline').eq(timeline);
$row.append($bar); // データの追加
saveData.schedule.push(data);
methods._saveData.apply($this, [saveData]); // コールバックがセットされていたら呼出
if (setting.onAppendSchedule) {
setting.onAppendSchedule.apply($this, [$bar, data]);
} // key
var key = saveData.schedule.length - 1;
$bar.data('sc_key', key);
$bar.on('mouseup', function () {
// コールバックがセットされていたら呼出
if (setting.onClick) {
if ($(this).data('dragCheck') !== true && $(this).data('resizeCheck') !== true) {
var $n = $(this);
var scKey = $n.data('sc_key');
setting.onClick.apply($this, [$n, saveData.schedule[scKey]]);
}
}
});
var $node = $this.find('.sc_bar');
var currentNode = null; // move node.
$node.draggable({
grid: [setting.widthTimeX, 1],
containment: $this.find('.sc_main'),
helper: 'original',
start: function start(event, ui) {
var node = {};
node.node = this;
node.offsetTop = ui.position.top;
node.offsetLeft = ui.position.left;
node.currentTop = ui.position.top;
node.currentLeft = ui.position.left;
node.timeline = methods._getTimeLineNumber.apply($this, [currentNode, ui.position.top]);
node.nowTimeline = node.timeline;
currentNode = node;
},
/**
*
* @param {Event} event
* @param {function} ui
* @returns {boolean}
*/
drag: function drag(event, ui) {
$(this).data('dragCheck', true);
if (!currentNode) {
return false;
}
var $moveNode = $(this);
var scKey = $moveNode.data('sc_key');
var timelineNum = methods._getTimeLineNumber.apply($this, [currentNode, ui.position.top]); // eslint-disable-next-line no-param-reassign
ui.position.left = Math.floor(ui.position.left / setting.widthTimeX) * setting.widthTimeX;
if (currentNode.nowTimeline !== timelineNum) {
// 現在のタイムライン
currentNode.nowTimeline = timelineNum;
}
currentNode.currentTop = ui.position.top;
currentNode.currentLeft = ui.position.left; // テキスト変更
methods._rewriteBarText.apply($this, [$moveNode, saveData.schedule[scKey]]);
return true;
},
// 要素の移動が終った後の処理
stop: function stop() {
$(this).data('dragCheck', false);
currentNode = null;
var $n = $(this);
var scKey = $n.data('sc_key');
var x = $n.position().left; // var w = $n.width();
var start = saveData.tableStartTime + Math.floor(x / setting.widthTimeX) * setting.widthTime; // var end = saveData.tableStartTime + (Math.floor((x + w) / setting.widthTimeX) * setting.widthTime);
var end = start + (saveData.schedule[scKey].endTime - saveData.schedule[scKey].startTime);
saveData.schedule[scKey].start = methods.formatTime(start);
saveData.schedule[scKey].end = methods.formatTime(end);
saveData.schedule[scKey].startTime = start;
saveData.schedule[scKey].endTime = end; // コールバックがセットされていたら呼出
if (setting.onChange) {
setting.onChange.apply($this, [$n, saveData.schedule[scKey]]);
}
}
});
var resizableHandles = ['e'];
if (setting.resizableLeft) {
resizableHandles.push('w');
}
$node.resizable({
handles: resizableHandles.join(','),
grid: [setting.widthTimeX, setting.timeLineY - setting.timeBorder],
minWidth: setting.widthTimeX,
containment: $this.find('.sc_main_scroll'),
start: function start() {
var $n = $(this);
$n.data('resizeCheck', true);
},
resize: function resize(ev, ui) {
// box-sizing: border-box; に対応
ui.element.height(ui.size.height);
ui.element.width(ui.size.width);
},
// 要素の移動が終った後の処理
stop: function stop() {
var $n = $(this);
var scKey = $n.data('sc_key');
var x = $n.position().left;
var w = $n.outerWidth();
var start = saveData.tableStartTime + Math.floor(x / setting.widthTimeX) * setting.widthTime;
var end = saveData.tableStartTime + Math.floor((x + w) / setting.widthTimeX) * setting.widthTime;
var timelineNum = saveData.schedule[scKey].timeline;
saveData.schedule[scKey].start = methods.formatTime(start);
saveData.schedule[scKey].end = methods.formatTime(end);
saveData.schedule[scKey].startTime = start;
saveData.schedule[scKey].endTime = end; // 高さ調整
methods._resetBarPosition.apply($this, [timelineNum]); // テキスト変更
methods._rewriteBarText.apply($this, [$n, saveData.schedule[scKey]]);
$n.data('resizeCheck', false); // コールバックがセットされていたら呼出
if (setting.onChange) {
setting.onChange.apply($this, [$n, saveData.schedule[scKey]]);
}
}
});
if (setting.draggable === false) {
$node.draggable('disable');
}
if (setting.resizable === false) {
$node.resizable('disable');
}
return key;
});
},
/**
* スケジュール数の取得
*
* @param {number} n row number
* @returns {number}
*/
_getScheduleCount: function _getScheduleCount(n) {
var $this = $(this);
var saveData = methods._loadData.apply($this);
var num = 0;
for (var i in saveData.schedule) {
if (saveData.schedule[i].timeline === n) {
num++;
}
}
return num;
},
/**
* add rows
*
* @param timeline
* @param row
*/
_addRow: function _addRow(timeline, row) {
return this.each(function () {
var $this = $(this);
var setting = methods._loadSettingData.apply($this);
var saveData = methods._loadData.apply($this);
var id = $this.find('.sc_main .timeline').length;
var html;
html = '';
html += '<div class="timeline"></div>';
var $data = $(html);
if (row.title) {
$data.append('<span class="timeline-title">' + row.title + '</span>');
}
if (row.subtitle) {
$data.append('<span class="timeline-subtitle">' + row.subtitle + '</span>');
} // event call
if (setting.onInitRow) {
setting.onInitRow.apply($this, [$data, row]);
}
$this.find('.sc_data_scroll').append($data);
html = '';
html += '<div class="timeline"></div>';
var $timeline = $(html);
for (var t = saveData.tableStartTime; t < saveData.tableEndTime; t += setting.widthTime) {
var $tl = $('<div class="tl"></div>');
$tl.outerWidth(setting.widthTimeX);
$tl.data('time', methods.formatTime(t));
$tl.data('timeline', timeline);
$timeline.append($tl);
} // クリックイベント
// left click
$timeline.find('.tl').on('click', function () {
if (setting.onScheduleClick) {
setting.onScheduleClick.apply($this, [this, $(this).data('time'), $(this).data('timeline'), saveData.timeline[$(this).data('timeline')]]);
}
}); // right click
$timeline.find('.tl').on('contextmenu', function () {
if (setting.onScheduleClick) {
setting.onScheduleClick.apply($this, [this, $(this).data('time'), $(this).data('timeline'), saveData.timeline[$(this).data('timeline')]]);
}
return false;
});
$this.find('.sc_main').append($timeline);
saveData.timeline[timeline] = row;
methods._saveData.apply($this, [saveData]);
if (row.class && row.class !== '') {
$this.find('.sc_data .timeline').eq(id).addClass(row.class);
$this.find('.sc_main .timeline').eq(id).addClass(row.class);
} // スケジュールタイムライン
if (row.schedule) {
for (var i in row.schedule) {
var bdata = row.schedule[i];
var s = bdata.start ? bdata.start : methods.calcStringTime(bdata.startTime);
var e = bdata.end ? bdata.end : methods.calcStringTime(bdata.endTime);
var data = {};
data.start = s;
data.end = e;
if (bdata.text) {
data.text = bdata.text;
}
data.timeline = timeline;
data.data = {};
if (bdata.data) {
data.data = bdata.data;
}
methods._addScheduleData.apply($this, [id, data]);
}
} // 高さの調整
methods._resetBarPosition.apply($this, [id]);
$this.find('.sc_main .timeline').eq(id).droppable({
accept: '.sc_bar',
drop: function drop(ev, ui) {
var node = ui.draggable;
var scKey = node.data('sc_key');
var nowTimelineNum = saveData.schedule[scKey].timeline;
var timelineNum = $this.find('.sc_main .timeline').index(this); // タイムラインの変更
saveData.schedule[scKey].timeline = timelineNum;
node.appendTo(this); // 高さ調整
methods._resetBarPosition.apply($this, [nowTimelineNum]);
methods._resetBarPosition.apply($this, [timelineNum]);
}
}); // コールバックがセットされていたら呼出
if (setting.onAppendRow) {
$this.find('.sc_main .timeline').eq(id).find('.sc_bar').each(function () {
var $n = $(this);
var scKey = $n.data('sc_key');
setting.onAppendRow.apply($this, [$n, saveData.schedule[scKey]]);
});
}
});
},
/**
* テキストの変更
*
* @param {jQuery} node
* @param {Object} data
*/
_rewriteBarText: function _rewriteBarText(node, data) {
return this.each(function () {
var $this = $(this);
var setting = methods._loadSettingData.apply($this);
var saveData = methods._loadData.apply($this);
var x = node.position().left; // var w = node.width();
var start = saveData.tableStartTime + Math.floor(x / setting.widthTimeX) * setting.widthTime; // var end = saveData.tableStartTime + (Math.floor((x + w) / setting.widthTimeX) * setting.widthTime);
var end = start + (data.endTime - data.startTime);
var html = methods.formatTime(start) + '-' + methods.formatTime(end);
$(node).find('.time').html(html);
});
},
/**
*
* @param {Number} n
*/
_resetBarPosition: function _resetBarPosition(n) {
return this.each(function () {
var $this = $(this);
var setting = methods._loadSettingData.apply($this); // 要素の並び替え
var $barList = $this.find('.sc_main .timeline').eq(n).find('.sc_bar');
var codes = [],
check = [];
var h = 0;
var $e1, $e2;
var c1, c2, s1, s2, e1, e2;
var i;
for (i = 0; i < $barList.length; i++) {
codes[i] = {
code: i,
x: $($barList[i]).position().left
};
} // ソート
codes.sort(function (a, b) {
if (a.x < b.x) {
return -1;
}
if (a.x > b.x) {
return 1;
}
return 0;
});
for (i = 0; i < codes.length; i++) {
c1 = codes[i].code;
$e1 = $($barList[c1]);
for (h = 0; h < check.length; h++) {
var next = false;
for (var j = 0; j < check[h].length; j++) {
c2 = check[h][j];
$e2 = $($barList[c2]);
s1 = $e1.position().left;
e1 = $e1.position().left + $e1.outerWidth();
s2 = $e2.position().left;
e2 = $e2.position().left + $e2.outerWidth();
if (s1 < e2 && e1 > s2) {
next = true;
continue;
}
}
if (!next) {
break;
}
}
if (!check[h]) {
check[h] = [];
}
$e1.css({
top: h * setting.timeLineY + setting.timeLinePaddingTop
});
check[h][check[h].length] = c1;
} // 高さの調整
methods._resizeRow.apply($this, [n, check.length]);
});
},
/**
*
* @param n
* @param height
*/
_resizeRow: function _resizeRow(n, height) {
return this.each(function () {
var $this = $(this);
var setting = methods._loadSettingData.apply($this);
var h = Math.max(height, 1);
$this.find('.sc_data .timeline').eq(n).outerHeight(h * setting.timeLineY + setting.timeLineBorder + setting.timeLinePaddingTop + setting.timeLinePaddingBottom);
$this.find('.sc_main .timeline').eq(n).outerHeight(h * setting.timeLineY + setting.timeLineBorder + setting.timeLinePaddingTop + setting.timeLinePaddingBottom);
$this.find('.sc_main .timeline').eq(n).find('.sc_bgBar').each(function () {
$(this).outerHeight($(this).closest('.timeline').outerHeight());
});
$this.find('.sc_data').outerHeight($this.find('.sc_main_box').outerHeight());
});
},
/**
* resizeWindow
*/
_resizeWindow: function _resizeWindow() {
return this.each(function () {
var $this = $(this);
var setting = methods._loadSettingData.apply($this);
var saveData = methods._loadData.apply($this);
var scWidth = $this.width();
var scMainWidth = scWidth - setting.dataWidth - setting.verticalScrollbar;
var cellNum = Math.floor((saveData.tableEndTime - saveData.tableStartTime) / setting.widthTime);
$this.find('.sc_header_cell').width(setting.dataWidth);
$this.find('.sc_data,.sc_data_scroll').width(setting.dataWidth);
$this.find('.sc_header').width(scMainWidth);
$this.find('.sc_main_box').width(scMainWidth);
$this.find('.sc_header_scroll').width(setting.widthTimeX * cellNum);
$this.find('.sc_main_scroll').width(setting.widthTimeX * cellNum);
});
},
/**
* move all cells of the right of the specified time line cell
*
* @param timeline
* @param baseTimeLineCell
* @param moveWidth
*/
_moveSchedules: function _moveSchedules(timeline, baseTimeLineCell, moveWidth) {
return this.each(function () {
var $this = $(this);
var setting = methods._loadSettingData.apply($this);
var saveData = methods._loadData.apply($this);
var $barList = $this.find('.sc_main .timeline').eq(timeline).find('.sc_bar');
for (var i = 0; i < $barList.length; i++) {
var $bar = $($barList[i]);
if (baseTimeLineCell.position().left <= $bar.position().left) {
var v1 = $bar.position().left + setting.widthTimeX * moveWidth;
var v2 = Math.floor((saveData.tableEndTime - saveData.tableStartTime) / setting.widthTime) * setting.widthTimeX - $bar.outerWidth();
$bar.css({
left: Math.max(0, Math.min(v1, v2))
});
var scKey = $bar.data('sc_key');
var start = saveData.tableStartTime + Math.floor($bar.position().left / setting.widthTimeX) * setting.widthTime;
var end = start + (saveData.schedule[scKey].end - saveData.schedule[scKey].start);
saveData.schedule[scKey].start = methods.formatTime(start);
saveData.schedule[scKey].end = methods.formatTime(end);
saveData.schedule[scKey].startTime = start;
saveData.schedule[scKey].endTime = end;
methods._rewriteBarText.apply($this, [$bar, saveData.schedule[scKey]]); // if setting
if (setting.onChange) {
setting.onChange.apply($this, [$bar, saveData.schedule[scKey]]);
}
}
}
methods._resetBarPosition.apply($this, [timeline]);
});
},
/**
* initialize
*/
init: function init(options) {
return this.each(function () {
var $this = $(this);
var config = $.extend({
className: 'jq-schedule',
rows: {},
startTime: '07:00',
endTime: '19:30',
widthTimeX: 25,
// 1cell辺りの幅(px)
widthTime: 600,
// 区切り時間(秒)
timeLineY: 50,
// timeline height(px)
timeLineBorder: 1,
// timeline height border
timeBorder: 1,
// border width
timeLinePaddingTop: 0,
timeLinePaddingBottom: 0,
headTimeBorder: 1,
// time border width
dataWidth: 90,
// data width
verticalScrollbar: 0,
// vertical scrollbar width
bundleMoveWidth: 1,
// width to move all schedules to the right of the clicked time cell
draggable: true,
resizable: true,
resizableLeft: false,
// event
onInitRow: null,
onChange: null,
onClick: null,
onAppendRow: null,
onAppendSchedule: null,
onScheduleClick: null
}, options);
methods._saveSettingData.apply($this, [config]);
var tableStartTime = methods.calcStringTime(config.startTime);
var tableEndTime = methods.calcStringTime(config.endTime);
tableStartTime -= tableStartTime % config.widthTime;
tableEndTime -= tableEndTime % config.widthTime;
methods._saveData.apply($this, [{
tableStartTime: tableStartTime,
tableEndTime: tableEndTime
}]);
var html = '' + '<div class="sc_menu">' + '\n' + '<div class="sc_header_cell"><span> </span></div>' + '\n' + '<div class="sc_header">' + '\n' + '<div class="sc_header_scroll"></div>' + '\n' + '</div>' + '\n' + '</div>' + '\n' + '<div class="sc_wrapper">' + '\n' + '<div class="sc_data">' + '\n' + '<div class="sc_data_scroll"></div>' + '\n' + '</div>' + '\n' + '<div class="sc_main_box">' + '\n' + '<div class="sc_main_scroll">' + '\n' + '<div class="sc_main"></div>' + '\n' + '</div>' + '\n' + '</div>' + '\n' + '</div>';
$this.append(html);
$this.addClass(config.className);
$this.find('.sc_main_box').on('scroll', function () {
$this.find('.sc_data_scroll').css('top', $(this).scrollTop() * -1);
$this.find('.sc_header_scroll').css('left', $(this).scrollLeft() * -1);
}); // add time cell
// var cellNum = Math.floor((tableEndTime - tableStartTime) / config.widthTime);
var beforeTime = -1;
for (var t = tableStartTime; t < tableEndTime; t += config.widthTime) {
if (beforeTime < 0 || Math.floor(beforeTime / 3600) !== Math.floor(t / 3600)) {
html = '';
html += '<div class="sc_time">' + methods.formatTime(t) + '</div>';
var $time = $(html);
var cn = Number(Math.min(Math.ceil((t + config.widthTime) / 3600) * 3600, tableEndTime) - t);
var cellNum = Math.floor(cn / config.widthTime);
$time.width(cellNum * config.widthTimeX);
$this.find('.sc_header_scroll').append($time);
beforeTime = t;
}
}
$(window).on('resize', function () {
methods._resizeWindow.apply($this);
}).trigger('resize'); // addrow
for (var i in config.rows) {
methods._addRow.apply($this, [i, config.rows[i]]);
}
});
}
};
/**
*
* @param {Object|string} method
* @returns {jQuery|methods|*}
*/
// eslint-disable-next-line no-param-reassign
$.fn.timeSchedule = function (method) {
// Method calling logic
if (methods[method]) {
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1)); // eslint-disable-next-line no-else-return
} else if (_typeof(method) === 'object' || !method) {
return methods.init.apply(this, arguments);
}
$.error('Method ' + method + ' does not exist on jQuery.timeSchedule');
return this;
};
})(jQuery);
</script>
<script type="text/javascript">
function addLog(type, message){
var $log = $('<tr />');
$log.append($('<th />').text(type));
$log.append($('<td />').text(message ? JSON.stringify(message) : ''));
$("#logs table").prepend($log);
}
$(function(){
$("#logs").append('<table class="table">');
var isDraggable = true;
var isResizable = true;
var $sc = $("#schedule").timeSchedule({
startTime: "07:00", // schedule start time(HH:ii)
endTime: "21:00", // schedule end time(HH:ii)
widthTime: 60 * 10, // cell timestamp example 10 minutes
timeLineY: 60, // height(px)
verticalScrollbar: 20, // scrollbar (px)
timeLineBorder: 2, // border(top and bottom)
bundleMoveWidth: 6, // width to move all schedules to the right of the clicked time line cell
draggable: isDraggable,
resizable: isResizable,
resizableLeft: true,
rows : {
'0' : {
title : 'Monday',
schedule:[
{
start: '09:00',
end: '12:00',
text: 'Text Area',
data: {
}
},
{
start: '11:00',
end: '14:00',
text: 'Text Area',
data: {
}
}
]
},
'1' : {
title : 'Tuesday',
schedule:[
{
start: '16:00',
end: '17:00',
text: 'Text Area',
data: {
}
}
]
},
'2' : {
title : 'Wednesday',
schedule:[
{
start: '16:00',
end: '17:00',
text: 'Text Area',
data: {
}
}
]
}
},
onChange: function(node, data){
addLog('onChange', data);
},
onInitRow: function(node, data){
addLog('onInitRow', data);
},
onClick: function(node, data){
addLog('onClick', data);
},
onAppendRow: function(node, data){
addLog('onAppendRow', data);
},
onAppendSchedule: function(node, data){
addLog('onAppendSchedule', data);
if(data.data.class){
node.addClass(data.data.class);
}
if(data.data.image){
var $img = $('<div class="photo"><img></div>');
$img.find('img').attr('src', data.data.image);
node.prepend($img);
node.addClass('sc_bar_photo');
}
},
onScheduleClick: function(node, time, timeline){
var start = time;
var end = $(this).timeSchedule('formatTime', $(this).timeSchedule('calcStringTime', time) + 3600);
$(this).timeSchedule('addSchedule', timeline, {
start: start,
end: end,
text:'Insert Schedule',
data:{
class: 'sc_bar_insert'
}
});
addLog('onScheduleClick', time + ' ' + timeline);
},
});
$('#event_timelineData').on('click', function(){
addLog('timelineData', $sc.timeSchedule('timelineData'));
});
$('#event_scheduleData').on('click', function(){
addLog('scheduleData', $sc.timeSchedule('scheduleData'));
});
$('#event_resetData').on('click', function(){
$sc.timeSchedule('resetData');
addLog('resetData');
});
$('#event_resetRowData').on('click', function(){
$sc.timeSchedule('resetRowData');
addLog('resetRowData');
});
$('#event_setDraggable').on('click', function(){
isDraggable = !isDraggable;
$sc.timeSchedule('setDraggable', isDraggable);
addLog('setDraggable', isDraggable ? 'enable' : 'disable');
});
$('#event_setResizable').on('click', function(){
isResizable = !isResizable;
$sc.timeSchedule('setResizable', isResizable);
addLog('setResizable', isResizable ? 'enable' : 'disable');
});
$('.ajax-data').on('click', function(){
$.ajax({url: './data/'+$(this).attr('data-target')})
.done( (data) => {
addLog('Ajax GetData', data);
$sc.timeSchedule('setRows', data);
});
});
$('#clear-logs').on('click', function(){
$('#logs .table').empty();
});
});
</script>
</body>
</html>
/* Add your styles here */
"use strict";
function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
(function ($) {
'use strict';
var PLUGIN_NAME = 'jqSchedule';
var methods = {
/**
*
* @param {string} str
* @returns {number}
*/
calcStringTime: function calcStringTime(str) {
var slice = str.split(':');
var h = Number(slice[0]) * 60 * 60;
var i = Number(slice[1]) * 60;
return h + i;
},
/**
*
* @param {number} val
* @returns {string}
*/
formatTime: function formatTime(val) {
var i1 = val % 3600;
var h = '' + Math.floor(val / 36000) + Math.floor(val / 3600 % 10);
var i = '' + Math.floor(i1 / 600) + Math.floor(i1 / 60 % 10);
return h + ':' + i;
},
/**
* 設定データの保存
*
* @param {Options} data
* @returns {*}
*/
_saveSettingData: function _saveSettingData(data) {
return this.data(PLUGIN_NAME + 'Setting', data);
},
/**
* 設定データの取得
*
* @returns Options
*/
_loadSettingData: function _loadSettingData() {
return this.data(PLUGIN_NAME + 'Setting');
},
/**
* 保存データの保存
*
* @param {SaveData} data
* @returns {*}
*/
_saveData: function _saveData(data) {
var d = $.extend({
tableStartTime: 0,
tableEndTime: 0,
schedule: [],
timeline: []
}, data);
return this.data(PLUGIN_NAME, d);
},
/**
* 保存データの取得
*
* @returns SaveData
*/
_loadData: function _loadData() {
return this.data(PLUGIN_NAME);
},
/**
* スケジュールの取得
*
* @returns ScheduleData[]
*/
scheduleData: function scheduleData() {
var $this = $(this);
var saveData = methods._loadData.apply($this);
if (saveData) {
return saveData.schedule;
}
return [];
},
/**
* get timelineData
* @returns {any[]}
*/
timelineData: function timelineData() {
var $this = $(this);
var saveData = methods._loadData.apply($this);
var data = [];
var i;
for (i in saveData.timeline) {
data[i] = saveData.timeline[i];
data[i].schedule = [];
}
for (i in saveData.schedule) {
var d = saveData.schedule[i];
if (typeof d.timeline === 'undefined') {
continue;
}
if (typeof data[d.timeline] === 'undefined') {
continue;
}
data[d.timeline].schedule.push(d);
}
return data;
},
/**
* reset data
*/
resetData: function resetData() {
return this.each(function () {
var $this = $(this);
var saveData = methods._loadData.apply($this);
saveData.schedule = [];
methods._saveData.apply($this, [saveData]);
$this.find('.sc_bar').remove();
for (var i in saveData.timeline) {
saveData.timeline[i].schedule = [];
methods._resizeRow.apply($this, [i, 0]);
}
methods._saveData.apply($this, [saveData]);
});
},
/**
* add schedule data
*
* @param {number} timeline
* @param {object} data
* @returns {methods}
*/
addSchedule: function addSchedule(timeline, data) {
return this.each(function () {
var $this = $(this);
var d = {
start: data.start,
end: data.end,
startTime: methods.calcStringTime(data.start),
endTime: methods.calcStringTime(data.end),
text: data.text,
timeline: timeline
};
if (data.data) {
d.data = data.data;
}
methods._addScheduleData.apply($this, [timeline, d]);
methods._resetBarPosition.apply($this, [timeline]);
});
},
/**
* add schedule data
*
* @param {number} timeline
* @param {object} data
* @returns {methods}
*/
addRow: function addRow(timeline, data) {
return this.each(function () {
var $this = $(this);
methods._addRow.apply($this, [timeline, data]);
});
},
/**
* clear row
*
* @returns {methods}
*/
resetRowData: function resetRowData() {
return this.each(function () {
var $this = $(this);
var data = methods._loadData.apply($this);
data.schedule = [];
data.timeline = [];
methods._saveData.apply($this, [data]);
$this.find('.sc_bar').remove();
$this.find('.timeline').remove();
$this.find('.sc_data').height(0);
});
},
/**
* clear row
*
* @param {object} data
* @returns {methods}
*/
setRows: function setRows(data) {
return this.each(function () {
var $this = $(this);
methods.resetRowData.apply($this, []);
for (var timeline in data) {
methods.addRow.apply($this, [timeline, data[timeline]]);
}
});
},
/**
* switch draggable
* @param {boolean} enable
*/
setDraggable: function setDraggable(enable) {
return this.each(function () {
var $this = $(this);
var setting = methods._loadSettingData.apply($this);
if (enable !== setting.draggable) {
setting.draggable = enable;
methods._saveSettingData.apply($this, setting);
if (enable) {
$this.find('.sc_bar').draggable('enable');
} else {
$this.find('.sc_bar').draggable('disable');
}
}
});
},
/**
* switch resizable
* @param {boolean} enable
*/
setResizable: function setResizable(enable) {
return this.each(function () {
var $this = $(this);
var setting = methods._loadSettingData.apply($this);
if (enable !== setting.resizable) {
setting.resizable = enable;
methods._saveSettingData.apply($this, setting);
if (enable) {
$this.find('.sc_bar').resizable('enable');
} else {
$this.find('.sc_bar').resizable('disable');
}
}
});
},
/**
* 現在のタイムライン番号を取得
*
* @param node
* @param top
* @returns {number}
*/
_getTimeLineNumber: function _getTimeLineNumber(node, top) {
var $this = $(this);
var setting = methods._loadSettingData.apply($this);
var num = 0;
var n = 0;
var tn = Math.ceil(top / (setting.timeLineY + setting.timeLinePaddingTop + setting.timeLinePaddingBottom));
for (var i in setting.rows) {
var r = setting.rows[i];
var tr = 0;
if (_typeof(r.schedule) === 'object') {
tr = r.schedule.length;
}
if (node && node.timeline) {
tr++;
}
n += Math.max(tr, 1);
if (n >= tn) {
break;
}
num++;
}
return num;
},
/**
* 背景データ追加
*
* @param {ScheduleData} data
*/
_addScheduleBgData: function _addScheduleBgData(data) {
return this.each(function () {
var $this = $(this);
var setting = methods._loadSettingData.apply($this);
var saveData = methods._loadData.apply($this);
var st = Math.ceil((data.startTime - saveData.tableStartTime) / setting.widthTime);
var et = Math.floor((data.endTime - saveData.tableStartTime) / setting.widthTime);
var $bar = $('<div class="sc_bgBar"><span class="text"></span></div>');
$bar.css({
left: st * setting.widthTimeX,
top: 0,
width: (et - st) * setting.widthTimeX,
height: $this.find('.sc_main .timeline').eq(data.timeline).height()
});
if (data.text) {
$bar.find('.text').text(data.text);
}
if (data.class) {
$bar.addClass(data.class);
} // $element.find('.sc_main').append($bar);
$this.find('.sc_main .timeline').eq(data.timeline).append($bar);
});
},
/**
* スケジュール追加
*
* @param timeline
* @param {ScheduleData} d
* @returns {number}
*/
_addScheduleData: function _addScheduleData(timeline, d) {
var data = d;
data.startTime = data.startTime ? data.startTime : methods.calcStringTime(data.start);
data.endTime = data.endTime ? data.endTime : methods.calcStringTime(data.end);
return this.each(function () {
var $this = $(this);
var setting = methods._loadSettingData.apply($this);
var saveData = methods._loadData.apply($this);
var st = Math.ceil((data.startTime - saveData.tableStartTime) / setting.widthTime);
var et = Math.floor((data.endTime - saveData.tableStartTime) / setting.widthTime);
var $bar = $('<div class="sc_bar"><span class="head"><span class="time"></span></span><span class="text"></span></div>');
var stext = methods.formatTime(data.startTime);
var etext = methods.formatTime(data.endTime);
var snum = methods._getScheduleCount.apply($this, [data.timeline]);
$bar.css({
left: st * setting.widthTimeX,
top: snum * setting.timeLineY + setting.timeLinePaddingTop,
width: (et - st) * setting.widthTimeX,
height: setting.timeLineY
});
$bar.find('.time').text(stext + '-' + etext);
if (data.text) {
$bar.find('.text').text(data.text);
}
if (data.class) {
$bar.addClass(data.class);
} // $this.find('.sc_main').append($bar);
var $row = $this.find('.sc_main .timeline').eq(timeline);
$row.append($bar); // データの追加
saveData.schedule.push(data);
methods._saveData.apply($this, [saveData]); // コールバックがセットされていたら呼出
if (setting.onAppendSchedule) {
setting.onAppendSchedule.apply($this, [$bar, data]);
} // key
var key = saveData.schedule.length - 1;
$bar.data('sc_key', key);
$bar.on('mouseup', function () {
// コールバックがセットされていたら呼出
if (setting.onClick) {
if ($(this).data('dragCheck') !== true && $(this).data('resizeCheck') !== true) {
var $n = $(this);
var scKey = $n.data('sc_key');
setting.onClick.apply($this, [$n, saveData.schedule[scKey]]);
}
}
});
var $node = $this.find('.sc_bar');
var currentNode = null; // move node.
$node.draggable({
grid: [setting.widthTimeX, 1],
containment: $this.find('.sc_main'),
helper: 'original',
start: function start(event, ui) {
var node = {};
node.node = this;
node.offsetTop = ui.position.top;
node.offsetLeft = ui.position.left;
node.currentTop = ui.position.top;
node.currentLeft = ui.position.left;
node.timeline = methods._getTimeLineNumber.apply($this, [currentNode, ui.position.top]);
node.nowTimeline = node.timeline;
currentNode = node;
},
/**
*
* @param {Event} event
* @param {function} ui
* @returns {boolean}
*/
drag: function drag(event, ui) {
$(this).data('dragCheck', true);
if (!currentNode) {
return false;
}
var $moveNode = $(this);
var scKey = $moveNode.data('sc_key');
var timelineNum = methods._getTimeLineNumber.apply($this, [currentNode, ui.position.top]); // eslint-disable-next-line no-param-reassign
ui.position.left = Math.floor(ui.position.left / setting.widthTimeX) * setting.widthTimeX;
if (currentNode.nowTimeline !== timelineNum) {
// 現在のタイムライン
currentNode.nowTimeline = timelineNum;
}
currentNode.currentTop = ui.position.top;
currentNode.currentLeft = ui.position.left; // テキスト変更
methods._rewriteBarText.apply($this, [$moveNode, saveData.schedule[scKey]]);
return true;
},
// 要素の移動が終った後の処理
stop: function stop() {
$(this).data('dragCheck', false);
currentNode = null;
var $n = $(this);
var scKey = $n.data('sc_key');
var x = $n.position().left; // var w = $n.width();
var start = saveData.tableStartTime + Math.floor(x / setting.widthTimeX) * setting.widthTime; // var end = saveData.tableStartTime + (Math.floor((x + w) / setting.widthTimeX) * setting.widthTime);
var end = start + (saveData.schedule[scKey].endTime - saveData.schedule[scKey].startTime);
saveData.schedule[scKey].start = methods.formatTime(start);
saveData.schedule[scKey].end = methods.formatTime(end);
saveData.schedule[scKey].startTime = start;
saveData.schedule[scKey].endTime = end; // コールバックがセットされていたら呼出
if (setting.onChange) {
setting.onChange.apply($this, [$n, saveData.schedule[scKey]]);
}
}
});
var resizableHandles = ['e'];
if (setting.resizableLeft) {
resizableHandles.push('w');
}
$node.resizable({
handles: resizableHandles.join(','),
grid: [setting.widthTimeX, setting.timeLineY - setting.timeBorder],
minWidth: setting.widthTimeX,
containment: $this.find('.sc_main_scroll'),
start: function start() {
var $n = $(this);
$n.data('resizeCheck', true);
},
resize: function resize(ev, ui) {
// box-sizing: border-box; に対応
ui.element.height(ui.size.height);
ui.element.width(ui.size.width);
},
// 要素の移動が終った後の処理
stop: function stop() {
var $n = $(this);
var scKey = $n.data('sc_key');
var x = $n.position().left;
var w = $n.outerWidth();
var start = saveData.tableStartTime + Math.floor(x / setting.widthTimeX) * setting.widthTime;
var end = saveData.tableStartTime + Math.floor((x + w) / setting.widthTimeX) * setting.widthTime;
var timelineNum = saveData.schedule[scKey].timeline;
saveData.schedule[scKey].start = methods.formatTime(start);
saveData.schedule[scKey].end = methods.formatTime(end);
saveData.schedule[scKey].startTime = start;
saveData.schedule[scKey].endTime = end; // 高さ調整
methods._resetBarPosition.apply($this, [timelineNum]); // テキスト変更
methods._rewriteBarText.apply($this, [$n, saveData.schedule[scKey]]);
$n.data('resizeCheck', false); // コールバックがセットされていたら呼出
if (setting.onChange) {
setting.onChange.apply($this, [$n, saveData.schedule[scKey]]);
}
}
});
if (setting.draggable === false) {
$node.draggable('disable');
}
if (setting.resizable === false) {
$node.resizable('disable');
}
return key;
});
},
/**
* スケジュール数の取得
*
* @param {number} n row number
* @returns {number}
*/
_getScheduleCount: function _getScheduleCount(n) {
var $this = $(this);
var saveData = methods._loadData.apply($this);
var num = 0;
for (var i in saveData.schedule) {
if (saveData.schedule[i].timeline === n) {
num++;
}
}
return num;
},
/**
* add rows
*
* @param timeline
* @param row
*/
_addRow: function _addRow(timeline, row) {
return this.each(function () {
var $this = $(this);
var setting = methods._loadSettingData.apply($this);
var saveData = methods._loadData.apply($this);
var id = $this.find('.sc_main .timeline').length;
var html;
html = '';
html += '<div class="timeline"></div>';
var $data = $(html);
if (row.title) {
$data.append('<span class="timeline-title">' + row.title + '</span>');
}
if (row.subtitle) {
$data.append('<span class="timeline-subtitle">' + row.subtitle + '</span>');
} // event call
if (setting.onInitRow) {
setting.onInitRow.apply($this, [$data, row]);
}
$this.find('.sc_data_scroll').append($data);
html = '';
html += '<div class="timeline"></div>';
var $timeline = $(html);
for (var t = saveData.tableStartTime; t < saveData.tableEndTime; t += setting.widthTime) {
var $tl = $('<div class="tl"></div>');
$tl.outerWidth(setting.widthTimeX);
$tl.data('time', methods.formatTime(t));
$tl.data('timeline', timeline);
$timeline.append($tl);
} // クリックイベント
// left click
$timeline.find('.tl').on('click', function () {
if (setting.onScheduleClick) {
setting.onScheduleClick.apply($this, [this, $(this).data('time'), $(this).data('timeline'), saveData.timeline[$(this).data('timeline')]]);
}
}); // right click
$timeline.find('.tl').on('contextmenu', function () {
if (setting.onScheduleClick) {
setting.onScheduleClick.apply($this, [this, $(this).data('time'), $(this).data('timeline'), saveData.timeline[$(this).data('timeline')]]);
}
return false;
});
$this.find('.sc_main').append($timeline);
saveData.timeline[timeline] = row;
methods._saveData.apply($this, [saveData]);
if (row.class && row.class !== '') {
$this.find('.sc_data .timeline').eq(id).addClass(row.class);
$this.find('.sc_main .timeline').eq(id).addClass(row.class);
} // スケジュールタイムライン
if (row.schedule) {
for (var i in row.schedule) {
var bdata = row.schedule[i];
var s = bdata.start ? bdata.start : methods.calcStringTime(bdata.startTime);
var e = bdata.end ? bdata.end : methods.calcStringTime(bdata.endTime);
var data = {};
data.start = s;
data.end = e;
if (bdata.text) {
data.text = bdata.text;
}
data.timeline = timeline;
data.data = {};
if (bdata.data) {
data.data = bdata.data;
}
methods._addScheduleData.apply($this, [id, data]);
}
} // 高さの調整
methods._resetBarPosition.apply($this, [id]);
$this.find('.sc_main .timeline').eq(id).droppable({
accept: '.sc_bar',
drop: function drop(ev, ui) {
var node = ui.draggable;
var scKey = node.data('sc_key');
var nowTimelineNum = saveData.schedule[scKey].timeline;
var timelineNum = $this.find('.sc_main .timeline').index(this); // タイムラインの変更
saveData.schedule[scKey].timeline = timelineNum;
node.appendTo(this); // 高さ調整
methods._resetBarPosition.apply($this, [nowTimelineNum]);
methods._resetBarPosition.apply($this, [timelineNum]);
}
}); // コールバックがセットされていたら呼出
if (setting.onAppendRow) {
$this.find('.sc_main .timeline').eq(id).find('.sc_bar').each(function () {
var $n = $(this);
var scKey = $n.data('sc_key');
setting.onAppendRow.apply($this, [$n, saveData.schedule[scKey]]);
});
}
});
},
/**
* テキストの変更
*
* @param {jQuery} node
* @param {Object} data
*/
_rewriteBarText: function _rewriteBarText(node, data) {
return this.each(function () {
var $this = $(this);
var setting = methods._loadSettingData.apply($this);
var saveData = methods._loadData.apply($this);
var x = node.position().left; // var w = node.width();
var start = saveData.tableStartTime + Math.floor(x / setting.widthTimeX) * setting.widthTime; // var end = saveData.tableStartTime + (Math.floor((x + w) / setting.widthTimeX) * setting.widthTime);
var end = start + (data.endTime - data.startTime);
var html = methods.formatTime(start) + '-' + methods.formatTime(end);
$(node).find('.time').html(html);
});
},
/**
*
* @param {Number} n
*/
_resetBarPosition: function _resetBarPosition(n) {
return this.each(function () {
var $this = $(this);
var setting = methods._loadSettingData.apply($this); // 要素の並び替え
var $barList = $this.find('.sc_main .timeline').eq(n).find('.sc_bar');
var codes = [],
check = [];
var h = 0;
var $e1, $e2;
var c1, c2, s1, s2, e1, e2;
var i;
for (i = 0; i < $barList.length; i++) {
codes[i] = {
code: i,
x: $($barList[i]).position().left
};
} // ソート
codes.sort(function (a, b) {
if (a.x < b.x) {
return -1;
}
if (a.x > b.x) {
return 1;
}
return 0;
});
for (i = 0; i < codes.length; i++) {
c1 = codes[i].code;
$e1 = $($barList[c1]);
for (h = 0; h < check.length; h++) {
var next = false;
for (var j = 0; j < check[h].length; j++) {
c2 = check[h][j];
$e2 = $($barList[c2]);
s1 = $e1.position().left;
e1 = $e1.position().left + $e1.outerWidth();
s2 = $e2.position().left;
e2 = $e2.position().left + $e2.outerWidth();
if (s1 < e2 && e1 > s2) {
next = true;
continue;
}
}
if (!next) {
break;
}
}
if (!check[h]) {
check[h] = [];
}
$e1.css({
top: h * setting.timeLineY + setting.timeLinePaddingTop
});
check[h][check[h].length] = c1;
} // 高さの調整
methods._resizeRow.apply($this, [n, check.length]);
});
},
/**
*
* @param n
* @param height
*/
_resizeRow: function _resizeRow(n, height) {
return this.each(function () {
var $this = $(this);
var setting = methods._loadSettingData.apply($this);
var h = Math.max(height, 1);
$this.find('.sc_data .timeline').eq(n).outerHeight(h * setting.timeLineY + setting.timeLineBorder + setting.timeLinePaddingTop + setting.timeLinePaddingBottom);
$this.find('.sc_main .timeline').eq(n).outerHeight(h * setting.timeLineY + setting.timeLineBorder + setting.timeLinePaddingTop + setting.timeLinePaddingBottom);
$this.find('.sc_main .timeline').eq(n).find('.sc_bgBar').each(function () {
$(this).outerHeight($(this).closest('.timeline').outerHeight());
});
$this.find('.sc_data').outerHeight($this.find('.sc_main_box').outerHeight());
});
},
/**
* resizeWindow
*/
_resizeWindow: function _resizeWindow() {
return this.each(function () {
var $this = $(this);
var setting = methods._loadSettingData.apply($this);
var saveData = methods._loadData.apply($this);
var scWidth = $this.width();
var scMainWidth = scWidth - setting.dataWidth - setting.verticalScrollbar;
var cellNum = Math.floor((saveData.tableEndTime - saveData.tableStartTime) / setting.widthTime);
$this.find('.sc_header_cell').width(setting.dataWidth);
$this.find('.sc_data,.sc_data_scroll').width(setting.dataWidth);
$this.find('.sc_header').width(scMainWidth);
$this.find('.sc_main_box').width(scMainWidth);
$this.find('.sc_header_scroll').width(setting.widthTimeX * cellNum);
$this.find('.sc_main_scroll').width(setting.widthTimeX * cellNum);
});
},
/**
* move all cells of the right of the specified time line cell
*
* @param timeline
* @param baseTimeLineCell
* @param moveWidth
*/
_moveSchedules: function _moveSchedules(timeline, baseTimeLineCell, moveWidth) {
return this.each(function () {
var $this = $(this);
var setting = methods._loadSettingData.apply($this);
var saveData = methods._loadData.apply($this);
var $barList = $this.find('.sc_main .timeline').eq(timeline).find('.sc_bar');
for (var i = 0; i < $barList.length; i++) {
var $bar = $($barList[i]);
if (baseTimeLineCell.position().left <= $bar.position().left) {
var v1 = $bar.position().left + setting.widthTimeX * moveWidth;
var v2 = Math.floor((saveData.tableEndTime - saveData.tableStartTime) / setting.widthTime) * setting.widthTimeX - $bar.outerWidth();
$bar.css({
left: Math.max(0, Math.min(v1, v2))
});
var scKey = $bar.data('sc_key');
var start = saveData.tableStartTime + Math.floor($bar.position().left / setting.widthTimeX) * setting.widthTime;
var end = start + (saveData.schedule[scKey].end - saveData.schedule[scKey].start);
saveData.schedule[scKey].start = methods.formatTime(start);
saveData.schedule[scKey].end = methods.formatTime(end);
saveData.schedule[scKey].startTime = start;
saveData.schedule[scKey].endTime = end;
methods._rewriteBarText.apply($this, [$bar, saveData.schedule[scKey]]); // if setting
if (setting.onChange) {
setting.onChange.apply($this, [$bar, saveData.schedule[scKey]]);
}
}
}
methods._resetBarPosition.apply($this, [timeline]);
});
},
/**
* initialize
*/
init: function init(options) {
return this.each(function () {
var $this = $(this);
var config = $.extend({
className: 'jq-schedule',
rows: {},
startTime: '07:00',
endTime: '19:30',
widthTimeX: 25,
// 1cell辺りの幅(px)
widthTime: 600,
// 区切り時間(秒)
timeLineY: 50,
// timeline height(px)
timeLineBorder: 1,
// timeline height border
timeBorder: 1,
// border width
timeLinePaddingTop: 0,
timeLinePaddingBottom: 0,
headTimeBorder: 1,
// time border width
dataWidth: 160,
// data width
verticalScrollbar: 0,
// vertical scrollbar width
bundleMoveWidth: 1,
// width to move all schedules to the right of the clicked time cell
draggable: true,
resizable: true,
resizableLeft: false,
// event
onInitRow: null,
onChange: null,
onClick: null,
onAppendRow: null,
onAppendSchedule: null,
onScheduleClick: null
}, options);
methods._saveSettingData.apply($this, [config]);
var tableStartTime = methods.calcStringTime(config.startTime);
var tableEndTime = methods.calcStringTime(config.endTime);
tableStartTime -= tableStartTime % config.widthTime;
tableEndTime -= tableEndTime % config.widthTime;
methods._saveData.apply($this, [{
tableStartTime: tableStartTime,
tableEndTime: tableEndTime
}]);
var html = '' + '<div class="sc_menu">' + '\n' + '<div class="sc_header_cell"><span> </span></div>' + '\n' + '<div class="sc_header">' + '\n' + '<div class="sc_header_scroll"></div>' + '\n' + '</div>' + '\n' + '</div>' + '\n' + '<div class="sc_wrapper">' + '\n' + '<div class="sc_data">' + '\n' + '<div class="sc_data_scroll"></div>' + '\n' + '</div>' + '\n' + '<div class="sc_main_box">' + '\n' + '<div class="sc_main_scroll">' + '\n' + '<div class="sc_main"></div>' + '\n' + '</div>' + '\n' + '</div>' + '\n' + '</div>';
$this.append(html);
$this.addClass(config.className);
$this.find('.sc_main_box').on('scroll', function () {
$this.find('.sc_data_scroll').css('top', $(this).scrollTop() * -1);
$this.find('.sc_header_scroll').css('left', $(this).scrollLeft() * -1);
}); // add time cell
// var cellNum = Math.floor((tableEndTime - tableStartTime) / config.widthTime);
var beforeTime = -1;
for (var t = tableStartTime; t < tableEndTime; t += config.widthTime) {
if (beforeTime < 0 || Math.floor(beforeTime / 3600) !== Math.floor(t / 3600)) {
html = '';
html += '<div class="sc_time">' + methods.formatTime(t) + '</div>';
var $time = $(html);
var cn = Number(Math.min(Math.ceil((t + config.widthTime) / 3600) * 3600, tableEndTime) - t);
var cellNum = Math.floor(cn / config.widthTime);
$time.width(cellNum * config.widthTimeX);
$this.find('.sc_header_scroll').append($time);
beforeTime = t;
}
}
$(window).on('resize', function () {
methods._resizeWindow.apply($this);
}).trigger('resize'); // addrow
for (var i in config.rows) {
methods._addRow.apply($this, [i, config.rows[i]]);
}
});
}
};
/**
*
* @param {Object|string} method
* @returns {jQuery|methods|*}
*/
// eslint-disable-next-line no-param-reassign
$.fn.timeSchedule = function (method) {
// Method calling logic
if (methods[method]) {
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1)); // eslint-disable-next-line no-else-return
} else if (_typeof(method) === 'object' || !method) {
return methods.init.apply(this, arguments);
}
$.error('Method ' + method + ' does not exist on jQuery.timeSchedule');
return this;
};
})(jQuery);