function is_touch_device() { var prefixes = ' -webkit- -moz- -o- -ms- '.split(' '); var mq = function(query) { return window.matchMedia(query).matches; } if (('ontouchstart' in window) || window.DocumentTouch && document instanceof window.DocumentTouch) { return true; } // include the 'heartz' as a way to have a non matching MQ to help terminate the join // https://git.io/vznFH var query = ['(', prefixes.join('touch-enabled),('), 'heartz', ')'].join(''); return mq(query); } function reloadWorldMap(option, drawTarget) { if (drawTarget === undefined || drawTarget === null) { drawTarget = '.world_map' } var $world_map = $(drawTarget); if ($world_map.length == 0) { return; } var defaultOption = { isDetailMap: true, //복잡 지도, 단순 지도 clickableAll: false, //어떤 경우든 클릭을 가능하게 함. 해당 동작의 동작 가능성 여부와는 별도. selectCallback: null, //callback을 지정시 clickable과 관계 없이 해당 함수를 실행. hrefTemplate: '#', //도시가 클릭가능할 경우 지정할 href값. {0}은 도시 id로 변환됨 useCachedMap: false, //맵 캐시를 사용 //아래부터는 post query에 들어갈 녀석 year: null, //year값, 연감등에 사용 month: null, //month값, 연감등에 사용 aux: null, //기타 넣고 싶은 값을 입력 neutralView: false, //clickable, 소속 국가, 첩보 여부 등을 반환여부를 설정 showMe: true, //반환 값에 본인이 위치한 도시 값을 반환하도록 설정. neutralView와 별개 targetJson: 'j_map.php', reqType: 'post', dynamicMapTheme: false, callback: null, //기타 보조 값 startYear: null, }; option = $.extend(defaultOption, option); var useCachedMap = option.useCachedMap; var isDetailMap = option.isDetailMap; var clickableAll = option.clickableAll; var selectCallback = option.selectCallback; var hrefTemplate = option.hrefTemplate; var cityPosition = getCityPosition(); var storedOldMapKey = 'sam.{0}.map'.format(serverNick); var storedStartYear = 'sam.{0}.startYear'.format(serverNick); //OBJ : startYear, year, month, cityList, nationList, spyList, shownByGeneralList, myCity function checkReturnObject(obj) { if (!obj.result) { var obj = $.Deferred(); obj.reject('fail'); return obj.promise(); } if (!$.isNumeric(obj.startYear) || !$.isNumeric(obj.year) || !$.isNumeric(obj.month) ) { var obj = $.Deferred(); obj.reject('fail'); return obj.promise(); } if (useCachedMap) { localStorage.setItem(storedOldMapKey, JSON.stringify([serverID, obj])); localStorage.setItem(storedStartYear, JSON.stringify(obj.startYear)); } $world_map.removeClass('draw_required'); return obj; } function setMapBackground(obj) { function setTheme() { var oldTheme = $world_map.data('currentTheme'); var newTheme = obj.theme; if (oldTheme === newTheme) { return; } if (oldTheme) { $world_map.removeClass('map_theme_' + oldTheme); } $world_map.addClass('map_theme_' + newTheme); $world_map.data('currentTheme', newTheme); } if (option.dynamicMapTheme) { setTheme(); } var startYear = obj.startYear; var year = obj.year; var month = obj.month; if (isDetailMap) { $world_map.addClass('map_detail').removeClass('map_basic'); } else { $world_map.addClass('map_basic').removeClass('map_detail'); } var $map_title = $('.map_title_text'); if (year < startYear + 1) { $map_title.css('color', 'magenta'); } else if (year < startYear + 2) { $map_title.css('color', 'orange'); } else if (year < startYear + 3) { $map_title.css('color', 'yellow'); } $map_title_tooltip = $('.map_title .tooltiptext'); $map_title_tooltip.empty(); var tooltipTexts = []; if (year < startYear + 3) { var startYearText = []; var remainYear = startYear + 3 - year; var remainMonth = 12 - month + 1; if (remainMonth > 0) { remainYear -= 1; } if (remainYear) { startYearText.push('{0}년'.format(remainYear)); } if (remainMonth) { startYearText.push('{0}개월'.format(remainMonth)); } tooltipTexts.push('초반제한 기간 : {0} ({1}년)'.format(startYearText.join(' '), startYear + 3)); } var currentTechLimit = Math.floor(Math.max(0, year - startYear) / 5) + 1; var nextTechLimitYear = currentTechLimit * 5 + startYear; tooltipTexts.push('기술등급 제한 : {0}등급 ({1}년 해제)'.format(currentTechLimit, nextTechLimitYear, currentTechLimit + 1)); $map_title_tooltip.html(tooltipTexts.join('
')); $world_map.removeClass('map_string map_summer map_fall map_winter'); if (month <= 3) { $world_map.addClass('map_spring'); } else if (month <= 6) { $world_map.addClass('map_summer'); } else if (month <= 9) { $world_map.addClass('map_fall'); } else { $world_map.addClass('map_winter'); } $map_title.html('{0}年 {1}月'.format(year, month)); return obj; } function convertCityObjs(obj) { //원본 Obj는 굉장히 간소하게 온다, Object 형태로 변환해서 사용한다. function toCityObj(arr) { return { id: arr[0], level: arr[1], state: arr[2], nationId: (arr[3] > 0) ? arr[3] : null, region: arr[4], supply: (arr[5] != 0) }; } function toNationObj(arr) { return { id: arr[0], name: arr[1], color: arr[2], capital: arr[3] }; } var cityList = obj.cityList.map(toCityObj); var nationList = obj.nationList.map(toNationObj); nationList = convertDictById(nationList); //array of object -> dict var spyList = obj.spyList; var shownByGeneralList = convertSet(obj.shownByGeneralList); //Array -> Set var myCity = obj.myCity; var myNation = obj.myNation; function mergePositionInfo(city) { var id = city.id; if (!(id in cityPosition)) { return city; } var xy_val = cityPosition[id]; city.name = xy_val[0]; city.x = xy_val[1]; city.y = xy_val[2]; return city; } function mergeNationInfo(city) { //nationId 값으로 isCapital, color, nation을 통합 var nationId = city.nationId; if (nationId === null || !(nationId in nationList)) { city.nationId = null; city.nation = null; city.color = null; city.isCapital = false; return city; } var nationObj = nationList[nationId]; city.nation = nationObj.name; city.color = nationObj.color; city.isCapital = (nationObj.capital == city.id); return city; } function mergeClickable(city) { //clickable = (defaultCity << 4 ) | (remainSpy << 3) | (ourCity << 2) | (shownByGeneral << 1) | clickableAll var id = city.id; var nationId = city.nationId; var clickable = 16; if (id in spyList) { clickable |= spyList[id] << 3; } if (myNation !== null && nationId == myNation) { clickable |= 4; } if (shownByGeneralList.hasOwnProperty(id)) { clickable |= 2; } if (myCity !== null && id == myCity) { clickable |= 2; } if (clickableAll) { clickable |= 1; } city.clickable = clickable; return city; } cityList = cityList .map(mergePositionInfo) .map(mergeNationInfo) .map(mergeClickable) .map(formatCityInfo); return { 'cityList': cityList, 'myCity': myCity }; } function drawDetailWorldMap(obj) { var $map_body = $(drawTarget + ' .map_body'); var cityList = obj.cityList; var myCity = obj.myCity; cityList.forEach(function(city) { var id = city.id; $('.city_base_{0}'.format(id)).detach(); //이전 도시는 지운다. var $cityObj = $('
'.format(id)); $cityObj.addClass('city_level_{0}'.format(city.level)); $cityObj.data('obj', city).css({ 'left': city.x - 20, 'top': city.y - 15 }); if ('color' in city && city.color !== null) { var $bgObj = $('
'); $cityObj.append($bgObj); $bgObj.css({ 'background-image': 'url({0}/b{1}.png)'.format(pathConfig.gameImage, convColorValue(city.color)) }); } var $linkObj = $(''); $linkObj.data({ 'text': city.text, 'nation': city.nation, 'id': city.id }); $cityObj.append($linkObj); var $imgObj = $('
'.format(pathConfig.gameImage, city.level)); $linkObj.append($imgObj); if (city.state > 0) { var $stateObj = $('
'.format(pathConfig.gameImage, city.state)); $linkObj.append($stateObj); } if (city.nationId && city.nationId > 0) { var flagType = city.supply ? 'f' : 'd'; var $flagObj = $('
'.format(pathConfig.gameImage, flagType, convColorValue(city.color))); if (city.isCapital) { var $capitalObj = $('
'.format(pathConfig.gameImage)); $flagObj.append($capitalObj); } $imgObj.append($flagObj); } var $nameObj = $('{0}'.format(city.name)); $imgObj.append($nameObj); $map_body.append($cityObj); }); $world_map.find('.city_base_{0} .city_filler'.format(myCity)).addClass('my_city'); return obj; } function drawBasicWorldMap(obj) { var $map_body = $(drawTarget + ' .map_body'); var cityList = obj.cityList; var myCity = obj.myCity; cityList.forEach(function(city) { var id = city.id; $('.city_base_{0}'.format(id)).detach(); //이전 도시는 지운다. var $cityObj = $('
'.format(id)); $cityObj.addClass('city_level_{0}'.format(city.level)); $cityObj.data('obj', city).css({ 'left': city.x - 20, 'top': city.y - 15 }); var $linkObj = $(''); $linkObj.data({ 'text': city.text, 'nation': city.nation, 'id': city.id }); $cityObj.append($linkObj); var $imgObj = $('
'); if ('color' in city && city.color !== null) { $imgObj.css({ 'background-color': city.color }); } $linkObj.append($imgObj); if (city.state > 0) { var state_text = 'wrong'; if (city.state < 10) { state_text = 'good'; } else if (city.state < 40) { state_text = 'bad'; } else if (city.state < 50) { state_text = 'war'; } var $stateObj = $('
'.format(state_text)); $imgObj.append($stateObj); } //단순 표기에서는 깃발 여부가 없음 if (city.isCapital) { var $capitalObj = $('
'); $imgObj.append($capitalObj); } var $nameObj = $('{0}'.format(city.name)); $imgObj.append($nameObj); $map_body.append($cityObj); }); $world_map.find('.city_base_{0} .city_filler'.format(myCity)).addClass('my_city'); return obj; } function setMouseWork(obj) { var $tooltip = $(drawTarget + ' .city_tooltip'); var $tooltip_city = $tooltip.find('.city_name'); var $tooltip_nation = $tooltip.find('.nation_name'); var $objs = $(drawTarget + ' .city_link'); var $map_body = $(drawTarget + ' .map_body'); //터치스크린 탭 if (!option.neutralView && is_touch_device()) { $objs.on('touchstart', function(e) { if (window.sam_toggleSingleTap) { return true; } var $this = $(this); var touchMode = $this.data('touchMode'); if ($tooltip_city.data('target') != $this.data('id')) { $this.data('touchMode', 1); } else if (touchMode === undefined) { $this.data('touchMode', 1); } else { $this.data('touchMode', touchMode + 1); } $map_body.data('touchMode', 1); $tooltip_city.data('target', $this.data('id')); }); $objs.on('touchend', function(e) { if (window.sam_toggleSingleTap) { return true; } var $this = $(this); var position = $this.parent().position(); $tooltip_city.html($this.data('text')); var nation_text = $this.data('nation'); if (nation_text) { $tooltip_nation.html(nation_text).show(); } else { $tooltip_nation.html('').hide(); } var left = position.left; var top = position.top; var scale = $map_body.data('scale'); if (scale) { left /= scale; top /= scale; } $tooltip.css({ 'top': top + 25, 'left': left + 35 }).show(); var touchMode = $this.data('touchMode'); if (touchMode <= 1) { return false; } //xxx: touchend 다음 click 이벤트가 갈 수도 있고, 안 갈 수도 있다. $this.data('touchMode', 0); }); $map_body.on('touchend', function(e) { if (window.sam_toggleSingleTap) { return true; } //위의 touchend bind에 해당하지 않는 경우 -> 빈 지도 터치 $tooltip.hide(); }); } //Mouse over 모드 작동 $map_body.on('mousemove', function(e) { if ($(this).data('touchMode')) { return true; } var rect = this.getBoundingClientRect(); var left = (e.clientX - rect.left - this.clientLeft + this.scrollLeft); var top = (e.clientY - rect.top - this.clientTop + this.scrollTop); var scale = $map_body.data('scale'); if (scale) { left /= scale; top /= scale; } $tooltip.css({ 'top': top + 10, 'left': left + 10 }); }); $objs.on('mouseenter', function(e) { if ($map_body.data('touchMode')) { return true; } var $this = $(this); $tooltip_city.data('target', $this.data('id')); $tooltip_city.html($this.data('text')); var nation_text = $this.data('nation'); if (nation_text) { $tooltip_nation.html(nation_text).show(); } else { $tooltip_nation.html('').hide(); } $tooltip.show(); }); $objs.on('mouseleave', function(event) { $tooltip.hide(); }); $objs.on('click', function(e) { //xxx: touchend 다음 click 이벤트가 갈 수도 있고, 안 갈 수도 있다. var touchMode = $(this).data('touchMode'); if (touchMode === undefined) { return; } if (touchMode === 1) { return false; } }); return obj; } function setCityClickable(obj) { obj.cityList.forEach(function(city) { var $cityLink = $world_map.find('.city_base_{0} .city_link'.format(city.id)); if ('clickable' in city && city.clickable > 0) { $cityLink.attr('href', hrefTemplate.format(city.id)); } if (selectCallback) { $cityLink.click(function() { return selectCallback(city); }); } }); return obj; } function saveCityInfo(obj) { $world_map.data('cityInfo', obj); return obj; } var $hideCityNameBtn = $world_map.find('.map_toggle_cityname'); if (localStorage.getItem('sam.hideMapCityName') == 'yes') { $world_map.addClass('hide_cityname'); $hideCityNameBtn.addClass('active').attr('aria-pressed', 'true'); } $hideCityNameBtn.click(function() { //이전 상태 확인 var state = !$hideCityNameBtn.hasClass('active'); if (state) { $world_map.addClass('hide_cityname'); localStorage.setItem('sam.hideMapCityName', 'yes'); } else { $world_map.removeClass('hide_cityname'); localStorage.setItem('sam.hideMapCityName', 'no'); } }); var $toggleSingleTapBtn = $world_map.find('.map_toggle_single_tap'); if (localStorage.getItem('sam.toggleSingleTap') == 'yes') { window.sam_toggleSingleTap = true; $toggleSingleTapBtn.addClass('active').attr('aria-pressed', 'true'); } else { window.sam_toggleSingleTap = false; } var $map_body = $(drawTarget + ' .map_body'); $toggleSingleTapBtn.click(function() { //이전 상태 확인 var state = !$toggleSingleTapBtn.hasClass('active'); if (state) { $map_body.removeData('touchMode'); localStorage.setItem('sam.toggleSingleTap', 'yes'); window.sam_toggleSingleTap = true; } else { localStorage.setItem('sam.toggleSingleTap', 'no'); window.sam_toggleSingleTap = false; } }); if (isDetailMap) { $world_map.addClass('map_detail'); } else { $world_map.removeClass('map_datail'); } //deferred mode of jQuery. != promise-then. var deferred = $.ajax({ url: option.targetJson, type: option.reqType, dataType: 'json', ifModified: true, data: { data: JSON.stringify({ neutralView: option.neutralView, year: option.year, month: option.month, showMe: option.showMe, aux: option.aux }) } }); var rawObject = null; deferred .then(function(data) { rawObject = data; return data; }) .then(checkReturnObject) .then(setMapBackground) .then(convertCityObjs) .then(isDetailMap ? drawDetailWorldMap : drawBasicWorldMap) .then(setMouseWork) .then(setCityClickable) .then(saveCityInfo) .then(function(data) { if (option.callback) { option.callback(data, rawObject); } }); if ($world_map.hasClass('draw_required')) { if (useCachedMap) { //일단 불러옴 do { var storedMap = localStorage.getItem(storedOldMapKey); if (!storedMap) { break; } storedMap = JSON.parse(storedMap); var storedServerID = storedMap[0]; if (storedServerID != serverID) { break; } storedMap = storedMap[1]; storedMap = setMapBackground(storedMap); storedMap = convertCityObjs(storedMap); if (isDetailMap) { storedMap = drawDetailWorldMap(storedMap); } else { storedMap = drawBasicWorldMap(storedMap); } storedMap = setMouseWork(storedMap); storedMap = setCityClickable(storedMap); storedMap = saveCityInfo(storedMap); } while (false); } else if (option.year && option.month) { var startYear = localStorage.getItem(storedStartYear); if (startYear) { startYear = JSON.parse(startYear); } else { startYear = option.year; } var tmp = { year: option.year, month: option.month, startYear: startYear }; setMapBackground(tmp); } } } $(function() { if (is_touch_device()) { $('.map_body .map_toggle_single_tap').show(); } })