TIP게시판

제목 nested panel - sk-rt / handy-collapse
글쓴이 darkninja 작성시각 2023/02/25 23:23:23
댓글 : 2 추천 : 0 스크랩 : 0 조회수 : 5783   RSS

https://github.com/sk-rt/handy-collapse

취미로 코딩을 하는 것은 정말 재미있고 흥미로운 것입니다.

초보자들이 코딩에 빠져들수 있는 재미를 가져볼수 있게 만들고 싶었는데

코드조각들이 점점 암호문처럼 되어가는.


<!DOCTYPE html>

<html>

<head>
	<meta charset="utf-8" />
	<title>Test page</title>

	<style>
		pre
		{
			padding: 0px;
			border-left: 3px solid #ccc;
			border-right: 2px solid #ccc;
			margin: 0px;
			overflow: auto;	width: 99.7%;
			background-color: #F5F5F5;
		}
	</style>

	<style>
		/* handy-collapse */
		/* top, right, bottom, left */

		.handy-panel
		{
			background: #eee;
			margin: 1px;
			border: 1px solid #CCC;
			-moz-border-radius: 3px;
			-webkit-border-radius: 3px;
		}

		.handy-subpanel
		{ 
			background: #eee;
			margin: 1px 10px 1px 10px;
			border: 1px solid #CCC;
			-moz-border-radius: 3px;
			-webkit-border-radius: 3px;
		}

		.handy-panel.collapsed,
		.handy-subpanel.collapsed
		{
			border: 1px solid #999;
			-moz-border-radius: 3px;
			-webkit-border-radius: 3px;
		}

		.handy-panel.expand,
		.handy-subpanel.expand
		{
			border-left: 1px solid #CCC;
			border-right: 1px solid #CCC;
			border-bottom: 1px solid #CCC;
			-moz-border-radius: 3px;
			-webkit-border-radius: 3px;
		}

		.handy-panel > button,
		.handy-subpanel > button
		{
			margin: 0px;
			padding: 2px 2px 2px 5px;
			background: #CCC url(images/arrow-up.gif) no-repeat 99.2% center;
			border: 0px;
			-moz-border-radius: 3px;
			-webkit-border-radius: 3px;
		}

		.handy-panel > button:hover,
		.handy-subpanel > button:hover { 
			background-color: #A9BCEF; 
			cursor:pointer;
		}

		.handy-panel.collapsed > button,
		.handy-subpanel.collapsed > button
		{
			margin: 0px;
			padding: 2px 2px 2px 5px;
			background: #CCC url(images/arrow-dn.gif) no-repeat 99.2% center;
			border-color: #CCC;
			-moz-border-radius: 3px;
			-webkit-border-radius: 3px;
		}

		.handy-panel.collapsed > button:hover,
		.handy-subpanel.collapsed > button:hover
		{
			margin: 0px;
			padding: 2px 2px 2px 5px;
			background: #A9BCEF url(images/arrow-dn.gif) no-repeat 99.2% center;
			-moz-border-radius: 3px;
			-webkit-border-radius: 3px;
		}

		.handy-panelcontent
		{ 
			padding: 2px 3px 2px 4px; 
			margin: 0px 0px 0px 0px;
			overflow: hidden;
			list-style-type: none;
		}

		.handy-panelcontent.is-1 { background-color:#FFFFFF; }
		.handy-panelcontent.is-2 { background-color:#F7F7F7; }
		.handy-panelcontent.is-3 { background-color:#EEEEEE; }
		.handy-panelcontent.is-4 { background-color:#E7E7E7; }
		.handy-panelcontent.is-5 { background-color:#CACACA; }


		.handy-panelcontent p
		{ 
			margin: 0px; 
			padding: 0px 0px 2px 0px; 
		}

		/* collapsed panel content */
		.handy-panel.collapsed .handy-panelcontent { display: none; }
		.handy-subpanel.collapsed .handy-panelcontent { display: none; }
	
		.button.is-fullwidth{
			display:flex;
			width:100%
		}
	</style>

</head>

<body>
	<?php
		//source
		//https://www.media-division.com/javascript-animated-collapsible-panels-without-frameworks/
		//https://github.com/sk-rt/handy-collapse
		//https://www.cssscript.com/nested-accordion-content-toggle-handy-collapse/
		//https://www.cssscript.com/demo/Nested-Accordion-Content-Toggle-handy-collapse/
		//https://stackoverflow.com/questions/6054033/pretty-printing-json-with-php
		//https://github.com/LorDOniX/json-viewer
		//https://github.com/JAAulde/cookies
	?>
	
	<div class="handy-panel left expand">
		<button type="button" class="button is-fullwidth" data-nested-button="handy-panel-nested">handy-panel-nested</button>
		<div class="handy-panelcontent is-1" data-nested-content="handy-panel-nested">
			<button type="button" id="loadjsondata">load jsondata</button>
		
			<div class="handy-panel left">
				<button type="button" class="button is-fullwidth" data-nested-button="handy-panel">handy-panel</button>
				<div class="handy-panelcontent is-1" data-nested-content="handy-panel">
					<div id="handycookiesArray" name="handycookiesArray"></div>
					<div id="allcookiesArray" name="allcookiesArray"></div>
				</div>
			</div>
			
			<div class="handy-panel left">
				<button type="button" class="button is-fullwidth" data-nested-button="jsonformatview">jsonformatview</button>
				<div class="handy-panelcontent is-1" data-nested-content="jsonformatview">
					<div id="disp_format_json" name="disp_format_json">0</div>
					<div id="disp_format_json1" name="disp_format_json1">1</div>
					<div id="disp_format_json2" name="disp_format_json2">2</div>
				</div>
			</div>

		</div>
	</div>

	<script type="text/javascript">
		var HANDY_PANEL           = "handy-panel";
		var HANDY_SUBPANEL        = "handy-subpanel";
		var HANDY_PANELCONTENT    = "handy-panelcontent";

		var HANDY_NAMESPACE_HEAD  = "data"
		var HANDY_NAMESPACE       = "basic"
		var HANDY_HEADING_TAG     = "button";
		var HANDY_CONTENT_TAG     = "content";
	
		var HANDY_EXPAND_CLASS    = "expand";
		var HANDY_COLLAPSED_CLASS = "collapsed";

		var HANDY_COOKIE_NAME     = "handy-panels";
		var HANDY_CSS_EASING      = "ease-in-out";

		var HANDY_ANIMATION_DELAY = 400; /*ms*/
		var HANDY_ANIMATION_STEPS = 10;

		var HANDY_PANEL_LEFT      = "left";
		var HANDY_PANEL_RIGHT     = "right";

		var _typeof = 
			typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? 
				function(obj) { return typeof obj; } : 
				function(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };

		var _extends = Object.assign || function(target) 
		{
			for (var i = 1; i < arguments.length; i++) {
				var source = arguments[i];
				for (var key in source) {
					if (Object.prototype.hasOwnProperty.call(source, key)) {
						target[key] = source[key];
					}
				}
			}
			return target;
		};

		var _createClass = function() 
		{
			function defineProperties(target, props) {
				for (var i = 0; i < props.length; i++) {
					var descriptor = props[i];
			
					descriptor.enumerable = descriptor.enumerable || false;
					descriptor.configurable = true;
			
					if ("value" in descriptor) descriptor.writable = true;
			
					Object.defineProperty(target, descriptor.key, descriptor);
				}
			}
			return function(Constructor, protoProps, staticProps) {
				if (protoProps) defineProperties(Constructor.prototype, protoProps);
				if (staticProps) defineProperties(Constructor, staticProps);
		
				return Constructor;
			};
		}();
 
		function _classCallCheck(instance, Constructor) 
		{
			if (!(instance instanceof Constructor)) {
				throw new TypeError("Cannot call a class as a function");
			}
		}

		var HandyCollapse = function() 
		{
			function HandyCollapse() 
			{
				var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
		
				_classCallCheck(this, HandyCollapse);
		
				_extends(this, {
					nameSpace: HANDY_NAMESPACE,
					toggleHeadingAttr: HANDY_NAMESPACE_HEAD + "-" + (options.nameSpace || HANDY_NAMESPACE) + "-" + HANDY_HEADING_TAG,
					toggleContentAttr: HANDY_NAMESPACE_HEAD + "-" + (options.nameSpace || HANDY_NAMESPACE) + "-" + HANDY_CONTENT_TAG,
					isAimation: true,
					closeOthers: true,
					animatinSpeed: HANDY_ANIMATION_DELAY,
					cssEasing: HANDY_CSS_EASING,
					cookie_name : HANDY_COOKIE_NAME + "-" + options.nameSpace,
					onSlideStart: function onSlideStart() {
						return false;
					},
					onSlideEnd: function onSlideEnd() {
						return false;
					}
				}, options);
		
				this.toggleHeadingEls = document.querySelectorAll("[" + this.toggleHeadingAttr + "]");
				this.toggleContentEls = document.querySelectorAll("[" + this.toggleContentAttr + "]");
				this.itemsStatus = {};

				this.loadSettings();
				this.init();
			}
	
			_createClass( HandyCollapse, 
				[	
					{
						key: "init",
						value: function init() {
							if (this.toggleHeadingEls) {
								this.setListner();
							}
							if (this.toggleContentEls) {
								this.setItem();
							}
							this.refresh();
						}
					}, 
					{
						key: "loadSettings",
						value: function loadSettings() {
							// prepare the object that will keep the panel statuses
							this.itemsStatus = {};

							var cookie = this.getcookie();
							if (cookie[0] == "empty") return;
	
							var cookieData = cookie[1];
							for( var key in cookieData ) {
								var value = cookieData[key];
								if (key == "undefined" || key == null || key == "") {
									//alert(key + " " + value);
								}	
								else {	
									this.itemsStatus[key] = value;
								}	
							}
						}
					}, 
					{
						key: "saveSettings",
						value: function saveSettings(id, expand) {
							// put the new expanding in the object
							this.itemsStatus[id] = expand;
	
							// create an array that will keep the contentid:expanding pairs
							var panelsData = [];
							for (var cid in this.itemsStatus) {
								if (cid == "undefined" || cid == null || cid == "") {
									//alert(cid + " " + this.itemsStatus[cid]);
								}	
								else {	
									panelsData.push(cid+":"+this.itemsStatus[cid]);
								}	
							}	
		
							// set the cookie expiration date 1 year from now
							var today = new Date();
							var expirationDate = new Date(today.getTime() + 1 * 1000 * 60 * 60 * 24);
						
							// write the cookie
							document.cookie = this.cookie_name + "=" + escape(panelsData.join(",")) + ";expires=" + expirationDate.toGMTString();
						}
					}, 
					{
						key: "getSubcookie",
						value: function getSubcookie(cookieValue) {
							var itemsArray = {};
							var cData = cookieValue.split(",");
							// split each key:value pair and put in object
							for (var i=0; i< cData.length; i++)
							{
								var pair = cData[i].split(":");
								itemsArray[pair[0]] = pair[1];
							}
							return itemsArray;
						}	
					}, 
					{
						key: "getAllcookies",
						value: function getAllcookies() {
							var c = [];
							if (document.cookie && document.cookie != '') {
								var split = document.cookie.split(';');
								for (var i = 0; i < split.length; i++) {
									var name_value = split[i].split("=");
									name_value[0] = name_value[0].replace(/^ /, '');
									
									var name = decodeURIComponent(name_value[0]);
									var value = this.getSubcookie(decodeURIComponent(name_value[1]));
									
									c.push([name, value]);
								}
							}
							else {
								c = { "empty": "cookie" };
							}	
							return c;
						}
					},	
					{
						key: "getcookie",
						value: function getcookie() {
							var c = this.getAllcookies();
							
							var cr = { "empty": "cookie" };
							for (var i = 0; i < c.length; i++) {

								//alert(c[i][0]);
								if (c[i][0] == this.cookie_name) { 
									cr = [ c[i][0], c[i][1] ];
									break;
								}	
								
							}
							return cr;
						}
					},	
					{
						key: "setListner",
						value: function setListner() {
							var _this2 = this;
							Array.prototype.slice.call(this.toggleHeadingEls).forEach(function(buttonEl, index) {
								var id = buttonEl.getAttribute(_this2.toggleHeadingAttr);
								if (id) {
									buttonEl.addEventListener("click", function(e) {
										e.preventDefault();
										_this2.toggleSlide(id, buttonEl);
									}, false);
								}
							});
						}
					}, 
					{
						key: "setItem",
						value: function setItem() {
							var _this = this;
							Array.prototype.slice.call(this.toggleContentEls).forEach(function(contentEl) {
								contentEl.style.maxHeight = "none";
							});
						}
					}, 
					{
						key: "setItemStatus",
						value: function setItemStatus(id, collapsed) {
							this.itemsStatus[id] = collapsed;
						}
					}, 
					{
						key: "expandAll",
						value: function expandAll() {
							for (var id in this.itemsStatus)
								this.setItemStatus(id, HANDY_EXPAND_CLASS);
		
							this.saveSettings();
							this.refresh();
						}
					}, 
					{
						key: "collapseAll",
						value: function collapseAll() {
							for (var id in this.itemsStatus)
								this.setItemStatus(id, HANDY_COLLAPSED_CLASS);
		
							this.saveSettings();
							this.refresh();
						}
					}, 
					{
						key: "refresh",
						value: function refresh() {
							var _this2 = this;
							Array.prototype.slice.call(this.toggleHeadingEls).forEach(function(buttonEl, index) {
								var id = buttonEl.getAttribute(_this2.toggleHeadingAttr);
								if (id) {
									// look for the id in loaded settings, apply the normal/collapsed class
									if (_this2.itemsStatus.hasOwnProperty(id)) {
										if (_this2.itemsStatus[id] == HANDY_EXPAND_CLASS) {
											buttonEl.parentNode.classList.remove(HANDY_COLLAPSED_CLASS);
											buttonEl.parentNode.classList.add(HANDY_EXPAND_CLASS);
										}	
										else {
											buttonEl.parentNode.classList.remove(HANDY_EXPAND_CLASS);
											buttonEl.parentNode.classList.add(HANDY_COLLAPSED_CLASS);
										}	
									}	
									else {
										// if no saved setting, see the initial setting
										_this2.itemsStatus[id] = 
											(buttonEl.parentNode.classList.contains(HANDY_EXPAND_CLASS)) ? HANDY_EXPAND_CLASS : HANDY_COLLAPSED_CLASS;
									}
						
									var expand = buttonEl.parentNode.classList.contains(HANDY_EXPAND_CLASS);

									_this2.setItemStatus(id, expand ? HANDY_EXPAND_CLASS : HANDY_COLLAPSED_CLASS);
								
									if (expand) {
										_this2.open(id, false, false);
									} else {
										_this2.close(id, false, false);
									}
								}
							});
						}
					}, 
					{
						key: "toggleSlide",
						value: function toggleSlide(id, buttonEl) {
							var isRunCallback = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
	
							if (this.itemsStatus[id] == HANDY_EXPAND_CLASS) {
								this.saveSettings(id, HANDY_COLLAPSED_CLASS);
								this.close(id, isRunCallback, this.isAimation);
							} else {
								this.saveSettings(id, HANDY_EXPAND_CLASS);
								this.open(id, isRunCallback, this.isAimation);
							}
						}
					}, 
					{
						key: "open",
						value: function open(id) {
							var _this3 = this;
							var isRunCallback = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
							var isAimation = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
							if (!id) return;
							if (this.closeOthers) {
								Array.prototype.slice.call(this.toggleHeadingEls).forEach(function(buttonEl, index) {
									var closeId = buttonEl.getAttribute(_this3.toggleHeadingAttr);
									if (closeId !== id) _this3.close(closeId, false, isAimation);
								});
							}
							if (isRunCallback !== false) this.onSlideStart(true, id);
							var toggleButton = document.querySelector("[" + this.toggleHeadingAttr + "='" + id + "']");
							var toggleContent = document.querySelector("[" + this.toggleContentAttr + "='" + id + "']");
							var clientHeight = this.getTargetHeight(toggleContent);
							toggleButton.parentNode.classList.remove(HANDY_COLLAPSED_CLASS);
							toggleButton.parentNode.classList.add(HANDY_EXPAND_CLASS);
							if (isAimation) {
								toggleContent.style.transition = this.animatinSpeed + "ms " + this.cssEasing;
								toggleContent.style.maxHeight = (clientHeight || "1000") + "px";
								setTimeout(function() {
									if (isRunCallback !== false) _this3.onSlideEnd(true, id);
									toggleContent.style.maxHeight = "none";
									toggleContent.style.transition = "";
								}, this.animatinSpeed);
							} else {
								toggleContent.style.maxHeight = "none";
							}
							this.itemsStatus[id] = HANDY_EXPAND_CLASS;
						}	
					}, 
					{
						key: "close",
						value: function close(id) {
							var _this4 = this;
							var isRunCallback = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
							var isAimation = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
							if (!id) return;
							if (isRunCallback !== false) this.onSlideStart(false, id);
							var toggleButton = document.querySelector("[" + this.toggleHeadingAttr + "='" + id + "']");
							var toggleContent = document.querySelector("[" + this.toggleContentAttr + "='" + id + "']");
							toggleButton.parentNode.classList.remove(HANDY_EXPAND_CLASS);
							toggleButton.parentNode.classList.add(HANDY_COLLAPSED_CLASS);
							toggleContent.style.maxHeight = toggleContent.clientHeight + "px";
							setTimeout(function() {
								toggleContent.style.maxHeight = "0px";
							}, 5);
							if (isAimation) {
								toggleContent.style.transition = this.animatinSpeed + "ms " + this.cssEasing;
								setTimeout(function() {
									if (isRunCallback !== false) _this4.onSlideEnd(false, id);
									toggleContent.style.transition = "";
								}, this.animatinSpeed);
							} else {
								this.onSlideEnd(false, id);
							}
							this.itemsStatus[id] = HANDY_COLLAPSED_CLASS;
						}
					}, 
					{
						key: "getTargetHeight",
						value: function getTargetHeight(targetEl) {
							if (!targetEl) return;
							var cloneEl = targetEl.cloneNode(true);
							var parentEl = targetEl.parentNode;
							cloneEl.style.maxHeight = "none";
							cloneEl.style.opacity = "0";
							parentEl.appendChild(cloneEl);
							targetEl.style.display = "block";
							var clientHeight = cloneEl.clientHeight;
							parentEl.removeChild(cloneEl);
							return clientHeight;
						}
					}
				]
			);
    
			return HandyCollapse;
		}();

		if ((typeof module === "undefined" ? "undefined" : _typeof(module)) === 'object') {
			module.exports = HandyCollapse;
		}
	</script>
	<script type="text/javascript">
        //Nested
        let handy_nested = new HandyCollapse({
            nameSpace: "nested",
            closeOthers: false
        });
	</script>

	<script type="text/javascript">
	</script>

	<script type="text/javascript">
		function formatJSON(jsonstr, html = false) {
			var tabcount = 0;
			var result = "";
			var inquote = false;
			var ignorenext = false;
			var tab = " ";
			var newline = "\n";
			if (html) {
				tab = "  ";
				newline = "<br/>";
			}
			for(var i = 0; i < jsonstr.length; i++) 
			{
				c = jsonstr[i];
				if (ignorenext) {
					result += c;
					ignorenext = false;
				} else {
					switch(c) {
						case '[':
						case '{':
							tabcount++;
							result += c + newline + tab.repeat( tabcount*2);
							break;
						case ']':
						case '}':
							tabcount--;
							result = result.trim() + newline + tab.repeat( tabcount*2) + c;
							break;
						case ',':
							result += c + newline + tab.repeat( tabcount*2);
							break;
						case '"':
							if ( !inquote ) {	
								if ( jsonstr[i-1] == ':' || jsonstr[i-2] == ':' )
									result += '<span style="color:#0000ff;">';
								else
									result += '<span style="color:#ff0000;">';
							}
							if (inquote)
								result += '</span>';
							inquote = !inquote;
							result += c;
							break;
						case '\\':
							if (inquote) ignorenext = true;
							result += c;
							break;
						default:
							result += c;
					}
				}
			}
			if (result == "")
				result = "no data...";
			else
				result = "<pre>"+result+"</pre>";

			return result;
		}
		
		var json_data1 = {a:1, 'b':'foo', c:[false, 'false', null, 'null', {d:{e:1.3e5, f:'1.3e5'}}]};
		var json_data2 = { "data": { "x": 1, "y": 1, "url": "http://url.com" }, "event": "start", "show": 1, "id": 50 };	
		var json_data = {
			key0: "<script>alert('no xss!')<\/script>",
			key1: "alert('no xss!')",
			key2: 12345,
			key3: new Date(),
			key4: [],
			key5: [
				123,
				"123",
				{
					a: 5,
					b: 6,
					c: null,
					d: true
				}
			],
			key6: {
				a: 1,
				b: 3,
				c: {
					d: 4
				}
			}
		};

		function all_cookiesArray() 
		{
			function getSubcookie(cookieValue) 
			{
				var itemsArray = {};
				var cData = cookieValue.split(",");
				// split each key:value pair and put in object
				for (var i=0; i< cData.length; i++)
				{
					var pair = cData[i].split(":");
					itemsArray[pair[0]] = pair[1];
				}
				return itemsArray;
			}
			var c = [];
			if (document.cookie && document.cookie != '') {
				var split = document.cookie.split(';');
				for (var i = 0; i < split.length; i++) {
					var name_value = split[i].split("=");
					name_value[0] = name_value[0].replace(/^ /, '');
					c.push(decodeURIComponent(name_value[0]), getSubcookie(decodeURIComponent(name_value[1])));
				}
			}
			else {
				c = { "empty": "cookie" };
			}	
			return c;
		}

		function loadjsondata() {
			var json_data_str = JSON.stringify(json_data);
			document.getElementById("disp_format_json").innerHTML  = "json_data<br>"+formatJSON(json_data_str);
			var json_data_str1 = JSON.stringify(json_data1);
			document.getElementById("disp_format_json1").innerHTML  = "json_data1<br>"+formatJSON(json_data_str1);
			var json_data_str2 = JSON.stringify(json_data2);
			document.getElementById("disp_format_json2").innerHTML  = "json_data2<br>"+formatJSON(json_data_str2);

			var handycookiesArray = handy_nested.getcookie();
			var handycookiesArray_str = JSON.stringify(handycookiesArray);
			document.getElementById("handycookiesArray").innerHTML  = "handycookiesArray<br>"+formatJSON(handycookiesArray_str);

			var allcookiesArray = all_cookiesArray();
			var allcookiesArray_str = JSON.stringify(allcookiesArray);
			document.getElementById("allcookiesArray").innerHTML  = "allcookiesArray<br>"+formatJSON(allcookiesArray_str);
		}

		loadjsondata();
	</script>

	<script type="text/javascript">
		document.getElementById("loadjsondata").addEventListener('click', function(event)
		{
			event.preventDefault();
			loadjsondata();
			return false;
		} );
	</script>

	</body>
</html>

 

첨부파일 handy-nested-panel.zip (5.9 KB)
 다음글 ci4 초 간단 csv 작성 [한글 안깨짐]
 이전글 Javascript animated collapsibl... (1)

댓글

darkninja / 2023/03/06 21:16:15 / 추천 0

지금 실력으로는 이것이 최선의 결과물입니다;

원소스의 'data-nested-control' => 'data-nested-button' 으로 바뀌었습니다.

쿠키 저장과 불러오는 부분이 수정되었습니다

쿠키저장 데이타의 형식은 확장성과 가독성을 봤을때 이 방법이 무난해 보입니다.

더 나은 방법이 있다면 알려주세요. 


<!DOCTYPE html>

<html>

<head>
	<meta charset="utf-8" />
	<title>Test page</title>

	<style>
		pre
		{
			padding: 0px;
			border-left: 3px solid #ccc;
			border-right: 2px solid #ccc;
			margin: 0px;
			overflow: auto;	width: 99.7%;
			background-color: #F5F5F5;
		}
	</style>

	<style>
/* handy-collapse */
/* top, right, bottom, left */

.handy-panel
{
	background: #eee;
	margin: 1px;
	border: 1px solid #CCC;
	-moz-border-radius: 3px;
	-webkit-border-radius: 3px;
}

.handy-subpanel
{ 
	background: #eee;
	margin: 1px 10px 1px 10px;
	border: 1px solid #CCC;
	-moz-border-radius: 3px;
	-webkit-border-radius: 3px;
}

.handy-panel.collapsed,
.handy-subpanel.collapsed
{
	border: 1px solid #999;
	-moz-border-radius: 3px;
	-webkit-border-radius: 3px;
}

.handy-panel.expand,
.handy-subpanel.expand
{
	border-left: 1px solid #CCC;
	border-right: 1px solid #CCC;
	border-bottom: 1px solid #CCC;
	-moz-border-radius: 3px;
	-webkit-border-radius: 3px;
}

.handy-panel > button,
.handy-subpanel > button
{
	margin: 0px;
	padding: 2px 2px 2px 5px;
	background: #CCC url(images/arrow-up.gif) no-repeat 99.2% center;
	border: 0px;
	-moz-border-radius: 3px;
	-webkit-border-radius: 3px;
}

.handy-panel > button:hover,
.handy-subpanel > button:hover { 
	background-color: #A9BCEF; 
	cursor:pointer;
}

.handy-panel.collapsed > button,
.handy-subpanel.collapsed > button
{
	margin: 0px;
	padding: 2px 2px 2px 5px;
	background: #CCC url(images/arrow-dn.gif) no-repeat 99.2% center;
	border-color: #CCC;
	-moz-border-radius: 3px;
	-webkit-border-radius: 3px;
}

.handy-panel.collapsed > button:hover,
.handy-subpanel.collapsed > button:hover
{
	margin: 0px;
	padding: 2px 2px 2px 5px;
	background: #A9BCEF url(images/arrow-dn.gif) no-repeat 99.2% center;
	-moz-border-radius: 3px;
	-webkit-border-radius: 3px;
}

.handy-panelcontent
{ 
	padding: 2px 3px 2px 4px; 
	margin: 0px 0px 0px 0px;
	overflow: hidden;
	list-style-type: none;
}

.handy-panelcontent.is-1 { background-color:#FFFFFF; }
.handy-panelcontent.is-2 { background-color:#F7F7F7; }
.handy-panelcontent.is-3 { background-color:#EEEEEE; }
.handy-panelcontent.is-4 { background-color:#E7E7E7; }
.handy-panelcontent.is-5 { background-color:#CACACA; }


.handy-panelcontent p
{ 
	margin: 0px; 
	padding: 0px 0px 2px 0px; 
}

/* collapsed panel content */
.handy-panel.collapsed .handy-panelcontent { display: none; }
.handy-subpanel.collapsed .handy-panelcontent { display: none; }

.button.is-fullwidth{
	display:flex;
	width:100%
}
	</style>

</head>

<body>
	<?php
		//source
		//https://www.media-division.com/javascript-animated-collapsible-panels-without-frameworks/
		//https://github.com/sk-rt/handy-collapse
		//https://www.cssscript.com/nested-accordion-content-toggle-handy-collapse/
		//https://www.cssscript.com/demo/Nested-Accordion-Content-Toggle-handy-collapse/
		//https://stackoverflow.com/questions/6054033/pretty-printing-json-with-php
		//https://github.com/LorDOniX/json-viewer
		//https://github.com/JAAulde/cookies
	?>
	
	<div class="handy-panel left expand">
		<button type="button" class="button is-fullwidth" data-nested-button="handy-panel-nested">handy-panel-nested</button>
		<div class="handy-panelcontent is-1" data-nested-content="handy-panel-nested">
			<button type="button" id="loadjsondata">load jsondata</button>
		
			<div class="handy-panel left">
				<button type="button" class="button is-fullwidth" data-nested-button="handy-panel">handy-panel</button>
				<div class="handy-panelcontent is-1" data-nested-content="handy-panel">
					<div id="handycookiesArray" name="handycookiesArray"></div>
					<div id="allcookiesArray" name="allcookiesArray"></div>
				</div>
			</div>
			
			<div class="handy-panel left">
				<button type="button" class="button is-fullwidth" data-nested-button="jsonformatview">jsonformatview</button>
				<div class="handy-panelcontent is-1" data-nested-content="jsonformatview">
					<div id="disp_format_json" name="disp_format_json">0</div>
					<div id="disp_format_json1" name="disp_format_json1">1</div>
					<div id="disp_format_json2" name="disp_format_json2">2</div>
				</div>
			</div>

		</div>
	</div>

	<script type="text/javascript">
var HANDY_PANEL           = "handy-panel";
var HANDY_SUBPANEL        = "handy-subpanel";
var HANDY_PANELCONTENT    = "handy-panelcontent";

var HANDY_NAMESPACE_HEAD  = "data"
var HANDY_NAMESPACE       = "basic"
var HANDY_HEADING_TAG     = "button";
var HANDY_CONTENT_TAG     = "content";

var HANDY_EXPAND_CLASS    = "expand";
var HANDY_COLLAPSED_CLASS = "collapsed";

var HANDY_COOKIE_NAME     = "handy-panels";
var HANDY_CSS_EASING      = "ease-in-out";

var HANDY_ANIMATION_DELAY = 400; /*ms*/
var HANDY_ANIMATION_STEPS = 10;

var HANDY_PANEL_LEFT      = "left";
var HANDY_PANEL_RIGHT     = "right";

var _typeof = 
	typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? 
		function(obj) { return typeof obj; } : 
		function(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };

var _extends = Object.assign || function(target) 
{
    for (var i = 1; i < arguments.length; i++) {
        var source = arguments[i];
        for (var key in source) {
            if (Object.prototype.hasOwnProperty.call(source, key)) {
                target[key] = source[key];
            }
        }
    }
    return target;
};

var _createClass = function() 
{
    function defineProperties(target, props) {
        for (var i = 0; i < props.length; i++) {
            var descriptor = props[i];
			
            descriptor.enumerable = descriptor.enumerable || false;
            descriptor.configurable = true;
			
            if ("value" in descriptor) descriptor.writable = true;
			
            Object.defineProperty(target, descriptor.key, descriptor);
        }
    }
    return function(Constructor, protoProps, staticProps) {
        if (protoProps) defineProperties(Constructor.prototype, protoProps);
        if (staticProps) defineProperties(Constructor, staticProps);
		
        return Constructor;
    };
}();
 
function _classCallCheck(instance, Constructor) 
{
    if (!(instance instanceof Constructor)) {
        throw new TypeError("Cannot call a class as a function");
    }
}

function getHandyPanelsHeight()
{
	var panelsList = document.getElementsByClassName(HANDY_PANEL);
	var panelsleft = new Array();
	var panelsright = new Array();
	var lh = 0;
	var rh = 0;
	var panel;

	for (var i=0; i<panelsList.length; i++)
	{
		panel = panelsList[i];
		if (panel.classList.contains(HANDY_PANEL_LEFT)) {
			lh += panel.offsetHeight;
		}	
		else if (panel.classList.contains(HANDY_PANEL_RIGHT)) {
			rh += panel.offsetHeight;
		}	
	}
	
	var lsw = document.getElementById('left_sidebar_wrapper');
	var lsw_h = window.getComputedStyle(lsw).height;
	lsw_h = Number(lsw_h.replace('px', ''));

	var rsw = document.getElementById('right_sidebar_wrapper');
	var rsw_h = window.getComputedStyle(rsw).height;
	rsw_h = Number(rsw_h.replace('px', ''));

	var big_lr = (lh>rh) ? lh : rh;
	var big_lswrsw = (lsw_h>rsw_h) ? lsw_h : rsw_h;
	var big = (big_lr>big_lswrsw) ? big_lr : big_lswrsw;

    var ret = [];
	ret['lh'] = lh;
	ret['rh'] = rh;
	ret['lsw_h'] = lsw_h;
	ret['rsw_h'] = rsw_h;
	ret['big_lr'] = big_lr;
	ret['big_lswrsw'] = big_lswrsw;
	ret['big'] = big;

	return ret;
}

var HandyCollapse = function() 
{
    function HandyCollapse() 
	{
        var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
		
        _classCallCheck(this, HandyCollapse);
		
        _extends(this, {
            nameSpace: HANDY_NAMESPACE,
            toggleHeadingAttr: HANDY_NAMESPACE_HEAD + "-" + (options.nameSpace || HANDY_NAMESPACE) + "-" + HANDY_HEADING_TAG,
            toggleContentAttr: HANDY_NAMESPACE_HEAD + "-" + (options.nameSpace || HANDY_NAMESPACE) + "-" + HANDY_CONTENT_TAG,
            isAimation: true,
            closeOthers: true,
            animatinSpeed: HANDY_ANIMATION_DELAY,
            cssEasing: HANDY_CSS_EASING,
			cookie_name : HANDY_COOKIE_NAME + "-" + options.nameSpace,
            onSlideStart: function onSlideStart() {
                return false;
            },
            onSlideEnd: function onSlideEnd() {
                return false;
            }
        }, options);
		
        this.toggleHeadingEls = document.querySelectorAll("[" + this.toggleHeadingAttr + "]");
        this.toggleContentEls = document.querySelectorAll("[" + this.toggleContentAttr + "]");
        this.itemsStatus = {};

		this.loadSettings();
        this.init();
    }
	
    _createClass( HandyCollapse, 
		[	
			{
				key: "init",
				value: function init() {
					if (this.toggleHeadingEls) {
						this.setListner();
					}
					if (this.toggleContentEls) {
						this.setItem();
					}
					this.refresh();
				}
			}, 
			{
				key: "loadSettings",
				value: function loadSettings() {
					// prepare the object that will keep the panel statuses
					this.itemsStatus = {};
					var s, state = this.getcookie();	
					if (state) {
                        try {
							s = JSON.parse(state);	
                        } catch (e1) {
                            s = null;
                        }
						if (s) {
							var sp = s.panelsData;		
							for (var i = 0; i < sp.length; i++) {
								var key = sp[i][0];
								var value = sp[i][1];
								if (key == "undefined" || key == null || key == "") {
									//alert(key + " " + value);
								}	
								else {	
									this.itemsStatus[key] = value;
								}	
							}	
						}
					}	
				}
			}, 
			{
				key: "saveSettings",
				value: function saveSettings(id, expand) {
					// put the new expanding in the object
					this.itemsStatus[id] = expand;
	
					// create an array that will keep the contentid:expanding pairs
					var panelsData = [];
					for (var cid in this.itemsStatus) {
						if (cid == "undefined" || cid == null || cid == "") {
							//alert(cid + " " + this.itemsStatus[cid]);
						}	
						else {	
							panelsData.push([cid, this.itemsStatus[cid]]);
						}	
					}	

					var state_json = {
						panelsData : panelsData
					};

					var state = JSON.stringify(state_json);
		
					// set the cookie expiration date 1 year from now
					var today = new Date();
					var expirationDate = new Date(today.getTime() + 1 * 1000 * 60 * 60 * 24);
					
					// write the cookie
					document.cookie = this.cookie_name + "=" + encodeURIComponent(state) + ";expires=" + expirationDate.toGMTString();
				}
			}, 
			{
				key: "getAllcookies",
				value: function getAllcookies() {
					var c = [];
					if (document.cookie && document.cookie != '') {
						var split = document.cookie.split(';');
						for (var i = 0; i < split.length; i++) {
							var name_value = split[i].split("=");
							name_value[0] = name_value[0].replace(/^ /, '');
								
							var name = decodeURIComponent(name_value[0]);
							var value = decodeURIComponent(name_value[1]);

							c[name] = value;
						}
					}
					return c;
				}
			},	
			{
				key: "getcookie",
				value: function getcookie() {
					var c = this.getAllcookies();
					var cr = [];
					for (var key in c) {
						if (key == this.cookie_name) { 
							cr = c[key];
							break;
						}	
					}
					return cr;
				}
			},	
			{
				key: "setListner",
				value: function setListner() {
					var _this2 = this;
					Array.prototype.slice.call(this.toggleHeadingEls).forEach(function(buttonEl, index) {
						var id = buttonEl.getAttribute(_this2.toggleHeadingAttr);
						if (id) {
							buttonEl.addEventListener("click", function(e) {
								e.preventDefault();
								_this2.toggleSlide(id, buttonEl);
							}, false);
						}
					});
				}
			}, 
			{
				key: "setItem",
				value: function setItem() {
					var _this = this;
					Array.prototype.slice.call(this.toggleContentEls).forEach(function(contentEl) {
						contentEl.style.maxHeight = "none";
					});
				}
			}, 
			{
				key: "setItemStatus",
				value: function setItemStatus(id, collapsed) {
					this.itemsStatus[id] = collapsed;
				}
			}, 
			{
				key: "expandAll",
				value: function expandAll() {
					for (var id in this.itemsStatus)
						this.setItemStatus(id, HANDY_EXPAND_CLASS);
		
					this.saveSettings();
					this.refresh();
				}
			}, 
			{
				key: "collapseAll",
				value: function collapseAll() {
					for (var id in this.itemsStatus)
						this.setItemStatus(id, HANDY_COLLAPSED_CLASS);
		
					this.saveSettings();
					this.refresh();
				}
			}, 
			{
				key: "refresh",
				value: function refresh() {
					var _this2 = this;
					Array.prototype.slice.call(this.toggleHeadingEls).forEach(function(buttonEl, index) {
						var id = buttonEl.getAttribute(_this2.toggleHeadingAttr);
						if (id) {

							// look for the id in loaded settings, apply the normal/collapsed class
							if (_this2.itemsStatus.hasOwnProperty(id)) {
								if (_this2.itemsStatus[id] == HANDY_EXPAND_CLASS) {
									buttonEl.parentNode.classList.remove(HANDY_COLLAPSED_CLASS);
									buttonEl.parentNode.classList.add(HANDY_EXPAND_CLASS);
								}	
								else {
									buttonEl.parentNode.classList.remove(HANDY_EXPAND_CLASS);
									buttonEl.parentNode.classList.add(HANDY_COLLAPSED_CLASS);
								}	
							}	
							else {
								// if no saved setting, see the initial setting
								_this2.itemsStatus[id] = 
									(buttonEl.parentNode.classList.contains(HANDY_EXPAND_CLASS)) ? HANDY_EXPAND_CLASS : HANDY_COLLAPSED_CLASS;
							}
						
							var expand = buttonEl.parentNode.classList.contains(HANDY_EXPAND_CLASS);

							_this2.setItemStatus(id, expand ? HANDY_EXPAND_CLASS : HANDY_COLLAPSED_CLASS);
							
							if (expand) {
								_this2.open(id, false, false);
							} else {
								_this2.close(id, false, false);
							}
						}
					});
				}
			}, 
			{
				key: "toggleSlide",
				value: function toggleSlide(id, buttonEl) {
					var isRunCallback = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;

					if (this.itemsStatus[id] == HANDY_EXPAND_CLASS) {
						this.saveSettings(id, HANDY_COLLAPSED_CLASS);
						this.close(id, isRunCallback, this.isAimation);
					} else {
						this.saveSettings(id, HANDY_EXPAND_CLASS);
						this.open(id, isRunCallback, this.isAimation);
					}
				}
			}, 
			{
				key: "open",
				value: function open(id) {
					var _this3 = this;
					var isRunCallback = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
					var isAimation = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
					if (!id) return;
					if (this.closeOthers) {
						Array.prototype.slice.call(this.toggleHeadingEls).forEach(function(buttonEl, index) {
							var closeId = buttonEl.getAttribute(_this3.toggleHeadingAttr);
							if (closeId !== id) _this3.close(closeId, false, isAimation);
						});
					}
					if (isRunCallback !== false) this.onSlideStart(true, id);
					var toggleButton = document.querySelector("[" + this.toggleHeadingAttr + "='" + id + "']");
					var toggleContent = document.querySelector("[" + this.toggleContentAttr + "='" + id + "']");
					var clientHeight = this.getTargetHeight(toggleContent);
					toggleButton.parentNode.classList.remove(HANDY_COLLAPSED_CLASS);
					toggleButton.parentNode.classList.add(HANDY_EXPAND_CLASS);
					if (isAimation) {
						toggleContent.style.transition = this.animatinSpeed + "ms " + this.cssEasing;
						toggleContent.style.maxHeight = (clientHeight || "1000") + "px";
						setTimeout(function() {
							if (isRunCallback !== false) _this3.onSlideEnd(true, id);
							toggleContent.style.maxHeight = "none";
							toggleContent.style.transition = "";
						}, this.animatinSpeed);
					} else {
						toggleContent.style.maxHeight = "none";
					}
					this.itemsStatus[id] = HANDY_EXPAND_CLASS;
				}
			}, 
			{
				key: "close",
				value: function close(id) {
					var _this4 = this;
					var isRunCallback = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
					var isAimation = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
					if (!id) return;
					if (isRunCallback !== false) this.onSlideStart(false, id);
					var toggleButton = document.querySelector("[" + this.toggleHeadingAttr + "='" + id + "']");
					var toggleContent = document.querySelector("[" + this.toggleContentAttr + "='" + id + "']");
					toggleButton.parentNode.classList.remove(HANDY_EXPAND_CLASS);
					toggleButton.parentNode.classList.add(HANDY_COLLAPSED_CLASS);
					toggleContent.style.maxHeight = toggleContent.clientHeight + "px";
					setTimeout(function() {
						toggleContent.style.maxHeight = "0px";
					}, 5);
					if (isAimation) {
						toggleContent.style.transition = this.animatinSpeed + "ms " + this.cssEasing;
						setTimeout(function() {
							if (isRunCallback !== false) _this4.onSlideEnd(false, id);
							toggleContent.style.transition = "";
						}, this.animatinSpeed);
					} else {
						this.onSlideEnd(false, id);
					}
					this.itemsStatus[id] = HANDY_COLLAPSED_CLASS;
				}
			}, 
			{
				key: "getTargetHeight",
				value: function getTargetHeight(targetEl) {
					if (!targetEl) return;
					var cloneEl = targetEl.cloneNode(true);
					var parentEl = targetEl.parentNode;
					cloneEl.style.maxHeight = "none";
					cloneEl.style.opacity = "0";
					parentEl.appendChild(cloneEl);
					targetEl.style.display = "block";
					var clientHeight = cloneEl.clientHeight;
					parentEl.removeChild(cloneEl);
					return clientHeight;
				}
			}
		]
	);
    
	return HandyCollapse;
}();

if ((typeof module === "undefined" ? "undefined" : _typeof(module)) === 'object') {
    module.exports = HandyCollapse;
}
	</script>
	<script type="text/javascript">
        //Nested
        let handy_nested = new HandyCollapse({
            nameSpace: "nested",
            closeOthers: false
        });
	</script>

	<script type="text/javascript">
	</script>

	<script type="text/javascript">
		function formatJSON(jsonstr, html = false) {
			var tabcount = 0;
			var result = "<pre>";
			var inquote = false;
			var ignorenext = false;
			var tab = " ";
			var newline = "\n";
			if (html) {
				tab = "    ";
				newline = "<br/>";
			}
			
			//alert(jsonstr);
			
			for(var i = 0; i < jsonstr.length; i++) 
			{
				c = jsonstr[i];
				if (ignorenext) {
					result += c;
					ignorenext = false;
				} else {
					switch(c) {
						case '[':
						case '{':
							tabcount++;
							result += c + newline + tab.repeat( tabcount*2);
							break;
						case ']':
						case '}':
							tabcount--;
							result = result.trim() + newline + tab.repeat( tabcount*2) + c;
							break;
						case ',':
							result += c + newline + tab.repeat( tabcount*2);
							break;
						case '"':
							if ( !inquote ) {	
								if ( jsonstr[i-1] == ':' || jsonstr[i-2] == ':' )
									result += '<span style="color:#0000ff;">';
								else
									result += '<span style="color:#ff0000;">';
							}
							if (inquote)
								result += '</span>';
							inquote = !inquote;
							result += c;
							break;
						case '\\':
							if (inquote) ignorenext = true;
							result += c;
							break;
						default:
							result += c;
					}
				}
			}
			result += "</pre>";
			return result;
		}
		
		var json_data1 = {a:1, 'b':'foo', c:[false, 'false', null, 'null', {d:{e:1.3e5, f:'1.3e5'}}]};
		var json_data2 = { "data": { "x": 1, "y": 1, "url": "http://url.com" }, "event": "start", "show": 1, "id": 50 };	
		var json_data = {
			key0: "<script>alert('no xss!')<\/script>",
			key1: "alert('no xss!')",
			key2: 12345,
			key3: new Date(),
			key4: [],
			key5: [
				123,
				"123",
				{
					a: 5,
					b: 6,
					c: null,
					d: true
				}
			],
			key6: {
				a: 1,
				b: 3,
				c: {
					d: 4
				}
			}
		};

		function all_cookiesArray() 
		{
			var c = [];
			if (document.cookie && document.cookie != '') {
				var split = document.cookie.split(';');
				for (var i = 0; i < split.length; i++) {
					var name_value = split[i].split("=");
					name_value[0] = name_value[0].replace(/^ /, '');
						
					c.push([decodeURIComponent(name_value[0]), decodeURIComponent(name_value[1])]);
				}
			}
			return c;
		}

		function loadjsondata() {
			var json_data_str = JSON.stringify(json_data);
			document.getElementById("disp_format_json").innerHTML  = "json_data<br>"+formatJSON(json_data_str);
			var json_data_str1 = JSON.stringify(json_data1);
			document.getElementById("disp_format_json1").innerHTML  = "json_data1<br>"+formatJSON(json_data_str1);
			var json_data_str2 = JSON.stringify(json_data2);
			document.getElementById("disp_format_json2").innerHTML  = "json_data2<br>"+formatJSON(json_data_str2);

			var handycookiesArray = ["handy_nested", handy_nested.getcookie()];
			var handycookiesArray_str = JSON.stringify(handycookiesArray);
			handycookiesArray_str = handycookiesArray_str.replaceAll('\\', '');
			document.getElementById("handycookiesArray").innerHTML  = "handycookiesArray<br>"+formatJSON(handycookiesArray_str);

			var cookiesgetAll = all_cookiesArray();
			var cookiescookiesgetAllstr = JSON.stringify(cookiesgetAll);
			cookiescookiesgetAllstr = cookiescookiesgetAllstr.replaceAll('\\', '');
			document.getElementById("allcookiesArray").innerHTML  = "disp format cookies<br>"+formatJSON(cookiescookiesgetAllstr);
		}

		loadjsondata();
	</script>

	<script type="text/javascript">
		document.getElementById("loadjsondata").addEventListener('click', function(event)
		{
			event.preventDefault();
			loadjsondata();
			return false;
		} );
	</script>

	</body>
</html>

 

darkninja / 2023/03/29 23:19:21 / 추천 0
	key: "saveSettings",
	value: function saveSettings(id, expand) {
                ...
                ...
                메인페이지에서는 쿠키가 작동하지만
                서브페이지에서는 제대로 일을 하지 않는데요...
                아래와 같이 "; path=/" 를 추가하여 쿠키저장위치를 모든 페이지에서 공통으로 
                사용하도록 해야 하는거 같습니다.
                이걸 안해줘도 이론적으로는 어느 위치에서 쿠키가 보관되어도
                정상작동해야 되지 않나 하는 생각도 들지만 현실은 그렇지 않으니.
                (우연히 그렇게 되는 경우도 있긴 한데)
                
                아래 코드로 수정한 후에
                Ctrl + Shift + Del 키를 눌러서 묵은 쿠키를 지워야
                그 다음 부터는 정상 동작합니다.

		// write the cookie
		document.cookie = this.cookie_name + "=" + encodeURIComponent(state) + "; expires=" + expirationDate.toGMTString() + "; path=/";
	}

        이제 하나를 배우면 하나를 까먹는.