/*===================================================== * Script Name: tour_module_suggest.js * Description: 空席照会モジュール サジェスト 共通処理 * Version: 1.01 * Last Up Date: 2017/10/25 =====================================================*/ var INPUT_LABEL_LENGTH = 25; var strategies = undefined; /** * util */ var isMobile = false; var isIE8 = function() { if (/msie 8\.0/i.test(navigator.userAgent)) { return true; } return false; }; var isIE7 = function() { if (/msie 7\.0/i.test(navigator.userAgent)) { return true; } return false; }; var isIE6 = function() { if (/msie 6\.0/i.test(navigator.userAgent)) { return true; } return false; }; var isFirefox = function() { if (/firefox/i.test(navigator.userAgent.toLowerCase())) { return true; } return false; }; var isDOMContentLoaded = false; $tour_module_jq.event.add(window,"load",function() { isDOMContentLoaded = true; }); (function($) { 'use strict'; //:focusable selector function focusable(element, isTabIndexNotNaN) { var map, mapName, img, nodeName = element.nodeName.toLowerCase(); if ('area' === nodeName) { map = element.parentNode; mapName = map.name; if (!element.href || !mapName || map.nodeName.toLowerCase() !== 'map') { return false; } img = $tour_module_jq('img[usemap=#' + mapName + ']')[0]; return !!img && visible(img); } return (/input|select|textarea|button|object/.test(nodeName) ? !element.disabled : 'a' === nodeName ? element.href || isTabIndexNotNaN : isTabIndexNotNaN) && visible(element); } function visible(element) { return $tour_module_jq.expr.filters.visible(element) && !$tour_module_jq(element).parents().addBack().filter(function() { return $tour_module_jq.css(this, 'visibility') === 'hidden'; }).length; } $tour_module_jq.extend($tour_module_jq.expr[':'], { data: function(elem, i, match) { return !!$tour_module_jq.data(elem, match[3]); }, focusable: function(element) { return focusable(element, !isNaN($tour_module_jq.attr(element, 'tabindex'))); } }); var chopText = function(text, length) { var _r = new RegExp('^.{1,' + length + '}'); text = text.match(_r); return text; }; /** * Bind the func to the context. */ var bind = function(func, context) { if (func.bind) { // Use native Function#bind if it's available. return func.bind(context); } else { return function() { func.apply(context, arguments); }; } }; /** * Get the styles of any element from property names. */ var getStyles = (function() { var color; color = $tour_module_jq('
').css(['color']).color; if (typeof color !== 'undefined') { return function($el, properties) { return $el.css(properties); }; } else { // for jQuery 1.8 or below return function($el, properties) { var styles; styles = {}; $tour_module_jq.each(properties, function(i, property) { styles[property] = $el.css(property); }); return styles; }; } })(); var ListView = (function() { function ListView($el, completer) { this.$el = $el; this.$wrapper = $tour_module_jq(this.$el).parents(completer.strategies.targetAutocompleteSelector); this.$resultWrapper = $tour_module_jq(this.$el).parents(completer.strategies.targetResultListWrapperSelector); this.$result = $tour_module_jq(this.$wrapper).find(completer.strategies.targetResultDataSelector); this.defaultOffset = 194; this.index = 0; this.completer = completer; this.scrollPosition = 0; this.wrapperHeight = this.$resultWrapper.height(); this.$el.on('mousedown', 'li', bind(this.onClick, this)); $tour_module_jq(window).on('resize', bind(this.deactivate,this)); }; $tour_module_jq.extend(ListView.prototype, { shown: false, render: function(data) { var uniq, label, html; this.index = 0; this.data = data; uniq = this.completer.strategies.uniq; label = this.completer.strategies.dispLabel; html = _.template(('<% _.each(d, function(_sugg) {%>' + '
  • ' + label + '
  • <% }); %>').replace(/<%= /g, '<%= _sugg.'), {d: data}).replace(/\[\d{3}\]/, '').replace(/\+\]/g, ']'); this.$el.append(html); this.scrollPosition = 0; if (data.length) { this.activateIndexedItem(); } }, clear: function() { this.$el.html(''); this.index = 0; return this; }, active: function() { if (!this.shown) { this.$el.show(); this.$wrapper.show(); this.shown = true; this.defaultOffset = this.getItem(0).offset().top; } return this; }, select: function(uniq) { this.completer.onSelect(uniq); this.completer.$el.blur(); this.deactivate(); }, deactivate: function() { if (this.shown) { this.$el.hide(); this.$wrapper.hide(); this.shown = false; this.data = this.index = null; } return this; }, activateIndexedItem: function() { this.$el.find('.is-select').removeClass('is-select'); this.getActiveItem().addClass('is-select'); }, getActiveItem: function() { return $tour_module_jq(this.$el.children().get(this.index)); }, getItem: function(index) { return $tour_module_jq(this.$el.children().get(index)); }, onKeydown: function(e) { if (!this.shown) return; if (e.keyCode === 27) { // ESC e.preventDefault(); this.deactivate(); } else if (e.keyCode === 38) { // UP e.preventDefault(); if (this.index === 0) { this.index = _.size(this.data) - 1; this.scrollPosition = this.getScrollBottomPosition() } else { this.index -= 1; } this.activateIndexedItem(); this.active(); var currentOffset = this.getActiveItem().offset().top; var activateItemHeight = this.getActiveItem().outerHeight(true); if ((currentOffset - this.defaultOffset) < 0) { this.scrollPosition = this.getActiveItem().position().top; } if (this.scrollPosition < 0) { this.scrollPosition = 0; } } else if (e.keyCode === 40) { // DOWN e.preventDefault(); if (this.index === _.size(this.data) - 1) { this.index = 0; this.scrollPosition = 0; } else { this.index += 1; } this.activateIndexedItem(); this.active(); var currentOffset = this.getActiveItem().offset().top; var activateItemHeight = this.getActiveItem().outerHeight(true); var offsetScrollPosition = currentOffset - this.defaultOffset + activateItemHeight - this.wrapperHeight; if (offsetScrollPosition > 0) { this.scrollPosition += offsetScrollPosition; } } else if (e.keyCode === 13 || e.keyCode === 9) { // ENTER or TAB e.preventDefault(); this.select(this.getActiveItem().attr('data-label')); } }, getScrollBottomPosition : function() { var result = 0; for (var i = 0; i < _.size(this.data); i++) { if (this.getItem(i).position().top + this.getItem(i).outerHeight(true) >= this.wrapperHeight) { result += this.getItem(i).outerHeight(true); } } return result; }, onClick: function(e) { var $e = $tour_module_jq(e.target); e.originalEvent.keepTextCompleteDropdown = true; if (!$e.hasClass('m_listItem')) { $e = $e.parents('li.m_listItem'); } this.select($e.attr('data-label')); } }); return ListView; })(); /** * Textarea manager class. * * @param {Object} $el 補完対象の * @param {Object} $target 送信されるデータが入る * @param {Object} strategies 補完処理の外部 */ var Completer = (function() { function Completer($el, target, strategies) { var $wrapper, $list, focused, $overlay, $clearBtn; var zindex; this.target = target; this.el = $el.get(0); // textarea element this.$el = $el; this.strategies = strategies; this.completed = false; //確定状態フラグ this.term = this.$el.val(); //クリアボタンがある状態と無い状態の横幅 this.elNorm = this.$el.width(); this.elClear = this.$el.width() - (parseInt(this.$el.css('font-size'), 10) * 2); //Elements $wrapper = $tour_module_jq(this.$el).parents(this.strategies.targetWrapperSelector); $list = $wrapper.find(this.strategies.targetResultListSelector); $overlay = $wrapper.find(this.strategies.targetTextOverlaySelector); $clearBtn = $wrapper.find(this.strategies.targetClearButtonSelector); //z-indexを調整する zindex = $wrapper.css('z-index') - $tour_module_jq('div.m_autocompleteWrapper').index($wrapper); $wrapper.css({'z-index': zindex}); this.overLay = $overlay; this.$clearBtn = $tour_module_jq($clearBtn).hide(); this.listView = new ListView($list, this); this.listView.clear(); //Events this.$el.on('keyup', bind(this.onKeyup, this)); this.$el.on('focus', bind(this.onFocus, this)); this.$el.on('blur', bind(this.onBlur, this)); this.$el.on('keydown', bind(this.onKeydown, this)); this.$el.on('keydown', bind(this.listView.onKeydown, this.listView)); this.$el.on('onSelect', bind(this.onSelect, this)); this.$el.on('clear', bind(this.clear, this)); if (isIE6() || isIE7() || isIE8() || isMobile) { this.$clearBtn.remove(); } var self = this; this.overLay.on('mousedown touchend', function(e) { //ie6 ie7 and tablet if (isIE6() || isIE7() || isIE8() || isMobile) { var focusableElements = $tour_module_jq(':focusable'), next = focusableElements.index(self.$el) + 1; focusableElements.eq(next).trigger('click'); return false; } $tour_module_jq(this).hide(); }); $tour_module_jq($clearBtn).on('click', bind(this.clear, this)); } $tour_module_jq.extend(Completer.prototype, { clear: function() { this.overLay.trigger('mousedown'); this.listView.deactivate(); this.suggestList = null; // this.$el.val(''); this.completed = false; this.overLay.text(''); this.$clearBtn.hide(); }, find: function(text) { var u = this.strategies.uniq; return _.find(this.strategies.dictionary, function(o) { return o[u] === text; }); }, doRule: function(keyEvent) { //ruleを評価して結果をキャッシュ var term = this.$el.val(); this.suggestList = []; if (term.length) { //if (keyEvent && this.term === term) return; // Ignore shift-key or something. this.term = term; this.suggestList = this.strategies.rule(this.term, this); return this.suggestList; } else { return false; } }, onBlur: function(e) { if (isIE6() || isIE7() || isIE8() || isMobile) { //何もしない } else if (!this.suggestList) { // 候補が存在しない場合はクリアする。 this.clear(); } else if (_.size(this.suggestList) == 1) { //候補が一つの場合は確定する。 this.onSelect(this.suggestList[0][this.strategies.uniq], e); } else if (_.size(this.suggestList) <= 0) { this.clear(); } var self = this; self.listView.deactivate(); e.preventDefault(); }, onFocus: function() { //ie6 ie7 and tablet if (isIE6() || isIE7() || isIE8() || isMobile) { var focusableElements = $tour_module_jq(':focusable'), next = focusableElements.index(this.$el) + 1; focusableElements.eq(next).trigger('click'); $tour_module_jq(this.$el).blur(); return false; } if (this.doRule() && _.size(this.suggestList) > 1) { this.listView.clear(); this.listView.render(this.suggestList); this.listView.active(); } else { this.listView.deactivate(); } }, onKeyup: function(e) { e.preventDefault(); if (this.doRule() && e.keyCode !== 38 && e.keyCode !== 40) { if (_.size(this.suggestList) > 0) { //todo this.listView.clear(); this.listView.render(this.suggestList); this.listView.active(); } else { this.listView.deactivate(); } } else { if (e.keyCode == 46 || e.keyCode == 8) { //BS or DEL this.listView.deactivate(); } } }, onKeydown: function(e) { if (e.keyCode == 46 || e.keyCode == 8) { //BS or DEL if (this.$el.val().length == this.$el.get(0).selectionStart && this.completed == true) { e.preventDefault(); this.clear(); } else { this.$clearBtn.hide(); this.completed = false; } } else { if (this.completed === true) { e.preventDefault(); } } }, onSelect: function(uid, ev) { var uid, label, ieLabel; if (uid == '') { return false; } label = _.template(this.strategies.label, this.find(uid)) .replace(/\[\d{3}\]$/, '').replace(/\+\]$/, ']'); if ((isIE6() || isIE7() || isIE8() || isMobile) && label.length >= INPUT_LABEL_LENGTH) { ieLabel = chopText(label, INPUT_LABEL_LENGTH) + '...'; } if (!ev && !isFirefox()) { ev = event; } this.completed = true; this.overLay.text(ieLabel ? ieLabel : label); this.overLay.show(); this.$el.val(label); this.listView.deactivate(); this.$clearBtn.show(); if (isFirefox()) { if (isDOMContentLoaded) { var focusableElements = $tour_module_jq(':focusable'); focusableElements.eq(focusableElements.index(this.$el) + 1).focus(); } } else { if (event != null && event.type !== 'DOMContentLoaded') { var focusableElements = $tour_module_jq(':focusable'); focusableElements.eq(focusableElements.index(this.$el) + 1).focus(); } } } }); /** * Completer's private functions */ var prepareWrapper = function($el) { return $baseWrapper.clone().css('display', $el.css('display')); }; return Completer; })(); strategies = function(args) { var defaults = { targetNode : '', targetWrapperSelector : 'div.m_autocompleteWrapper', targetAutocompleteSelector : 'div.m_autocomplete', targetResultDataSelector : 'span.m_resultData', targetResultListSelector : 'ul.m_resultList', targetResultListWrapperSelector : 'div.m_resultListWrapper', targetTextOverlaySelector : 'div.m_textOverlay', targetClearButtonSelector : 'a.m_clear', scrollInertia : 0, type: 'ja', uniq: 'course_base_cd', label: '<%= course_basic_name_jpn %>', //ラベルの書式フォーマット dispLabel: '<%= course_basic_name_jpn %>', //ラベルの書式フォーマット rule: function(text, obj) { var startingCharacterCount = m_inttourModulePkg_suggestSetting.startingCharacterCount; var startReg = new RegExp('^[\x20-\x7E]{' + startingCharacterCount + ',}.*$', 'g'); if (text.match(/^[^ -~。-゚]{1,}.*$/) || text.match(startReg)) { var self = this; var t = text.replace(/(\(|\)|\[|\]|\\|\^|\+|\*|\?)/g, '\\$&'); var r = new RegExp(t, 'i'); // 入力文字に一致する定義情報を絞込み var filterResult = _(this.dictionary).chain().filter(function(w) { if (w['course_basic_name_jpn'].match(r)) { return true; } else { return false; } }).value(); return filterResult; } else { return false; } }, sortDisplayList: function(displayList) { displayList.sort(function(item01,item02) { if( item01['order'] < item02['order'] ) return -1; if( item01['order'] > item02['order'] ) return 1; return 0; }); return displayList; }, getFirstFewDatas: function(displayList, count) { var firstFewDatas; firstFewDatas = displayList.slice(0, count); return firstFewDatas; }, dictionary: m_inttourModulePkg_TourNameJson['data'] }; $tour_module_jq.extend(true,this,defaults) $tour_module_jq.extend(true,this,args); return this; }; $tour_module_jq.fn.suggest = function(target, strategies) { $tour_module_jq(this).attr('autocomplete', 'off'); new Completer(this, target, strategies); return this; }; })(window.jQuery || window.Zepto);