Greasy Fork is available in English.

test_for_naver_land

Please use with violentmonkey

// ==UserScript==
// @name        test_for_naver_land
// @namespace   Violentmonkey Scripts
// @match       https://new.land.naver.com/complexes*
// @version     1.01
// @author      .
// @description Please use with violentmonkey
// @require     https://code.jquery.com/jquery-1.12.4.min.js
// @license     MIT
// ==/UserScript==




let isCreateCheckArea = false
let checkAreaValue = false

const AREA_CHECK = 'area_check';
const LOW_JEONSE_CHECK = 'low_jeonse_check'
const SEANGO_CHECK = 'seango_check'

const STORE_NAME = 'wolbu_price_filter'
const STORE_VALUE = { [AREA_CHECK]: true, [LOW_JEONSE_CHECK]: false, [SEANGO_CHECK]: true};

const validityCheck = {
  [AREA_CHECK] : { isCreate : false,  value: false, title: "35평이상 포함"}
  , [LOW_JEONSE_CHECK] : { isCreate : false,  value: false, title: "최저전세값"}
  , [SEANGO_CHECK] : { isCreate : false,  value: false, title: "세안고포함"}
}


// get local store value
function getStoreValue(id){

  let storeVal = localStorage.getItem(STORE_NAME);

  if(!storeVal){
    localStorage.setItem(STORE_NAME, JSON.stringify(STORE_VALUE) );
    storeVal = localStorage.getItem(STORE_NAME);
  }


  return JSON.parse(storeVal)[id]

}

// set local store value
function setStoreValue(id, val){

  let storeVal = localStorage.getItem(STORE_NAME)

  if(!storeVal)
      localStorage.setItem(STORE_NAME, JSON.stringify(STORE_VALUE) );

  let parseVal = JSON.parse(storeVal);
  parseVal[id] = val;
  localStorage.setItem(STORE_NAME, JSON.stringify(parseVal) );

}

function CheckBox(id, target){

  this.div_id = 'div_'+id;
  this.id = id;
  this.labelText = validityCheck[id].title;
  this.divEle = this.init();
  target.after(this.divEle);

  let storeVal = getStoreValue(this.id);
  validityCheck[id].value = storeVal
  document.querySelector('#'+id).checked = storeVal;

  document.querySelector('#'+id).addEventListener('change', function(e){
    validityCheck[id].value = this.checked;
    setStoreValue(id, this.checked)

  });
  validityCheck[id].isCreate = true;

}

CheckBox.prototype = {
  constructor : CheckBox
  , init: function(){

     const divEle = document.createElement('div');
     divEle.setAttribute('id', this.div_id)
     divEle.classList.add('filter_group', 'filter_group--size');
     divEle.style.margin= '6px 10px 0 0';
     divEle.innerHTML = '<input type="checkbox" name="type" id="'+this.id+'" class="checkbox_input" ><label for="'+this.id+'" class="checkbox_label">'+this.labelText+'</label>';
     return divEle;

  }
}



//   상황에 따라 분기가 필요할 수도
function createCheckBox(type){

  new CheckBox(type, document.querySelector('.filter_btn_detail'));


//   // 35평이상 포함여부
//   if(type ===  AREA_CHECK ){
//     new CheckBox(type, document.querySelector('.filter_btn_detail'));
//   }
//   else if(type ===  LOW_JEONSE_CHECK ){
//     new CheckBox(type, document.querySelector('.filter_btn_detail'));
//   }

}

function checkMandantoryCondition(size) {


    //  35평이상 포함여부 check
    //if(!validityCheck[AREA_CHECK].isCreate)      createCheckBox(AREA_CHECK);
    if( validityCheck[AREA_CHECK].value )   return true;


    // 35평 미만
    if (/\d+/g.exec(size) > (35 * 3.3)) {
        //console.log('Filtered by size - ', size);
        return false;
    }
    // todo : 300세대 미만, 용적률, 기타등등
    return true;
}

function getFloor(strFloor) {
    return strFloor.replace("층", "").split('/');
}

function checkItemCondition(tradeType, floor, spec) {

    //매매, 전세
    if (tradeType != "전세" && tradeType != "매매") {
        //console.log('Filtered by trade type - ', tradeType);
        return false;
    }

    // 세안고 제외
    if ( !validityCheck[SEANGO_CHECK].value && (spec.includes("끼고") || spec.includes("안고") || spec.includes("승계")) ) {
        //console.log('Filtered by spec - ', spec);
        return false;
    } else {
      //console.log('Allowed spec - ', spec);
    }

    // 층 - 전세의 경우 층에 관계없이 최고가 적용
    if (tradeType == "매매") {
        // 층 명확하지 않은 것 제외
        var _floorInfo = getFloor(floor);
        if (_floorInfo[0] == "저") {
            //console.log('Filtered by floor - ', _floorInfo);
            return false;
        }
        // 1층, 2층, 탑층 제외
        if ("1|2|3".indexOf(_floorInfo[0]) > -1 || _floorInfo[0] == _floorInfo[1]) {
            //console.log('Filtered by floor - ', _floorInfo);
            return false;
        }

        // 5층 이상 건물에서 3층 이하 제외
        if (_floorInfo[1] >= 5 && _floorInfo[0] <= 3) {
            //console.log('Filtered by floor - ', _floorInfo);
            return false;
        }
    }
    return true;
}

function parsePrice(tradePrice) {
    tradePrice = tradePrice.replace(" ", "").replace(",", "");
    if (tradePrice.includes("억"))
        return parseInt(tradePrice.split("억")[0] * 10000) + (parseInt(tradePrice.split("억")[1]) || 0);
    else
        return parseInt(tradePrice)
}

function getPrice_WeolbuStandard() {


    let result = {};
    let dictPricePerSize = {};
    let tradeTypeValueFnc = function( tradeType, befVal, newVal){

      let price, floor, spec;

      if( tradeType === '매매'){
          price = befVal[0] > newVal[0] ? newVal[0] :  befVal[0]
          floor = befVal[0] > newVal[0] ? newVal[1] :  befVal[1]
      }else {
          price = befVal[0] < newVal[0] ? newVal[0] :  befVal[0]
          floor = befVal[0] < newVal[0] ? newVal[1] :  befVal[1]
      }

      return [price, floor, befVal[2]+newVal[2], ++befVal[3] ];

    }



    document.querySelectorAll("#articleListArea > div").forEach(function(ele) {
        // console.log( ele.querySelectorAll("div.info_area .line .spec")[0].innerText)
        let aptInfo = ele.querySelectorAll("div.info_area .line .spec")[0].innerText.split(", ");
        let size = aptInfo[0];  // 103/84m^2
        let floor = aptInfo[1]; // 3/10층
        let tradeType = ele.querySelector("div.price_line .type").innerText; // 매매, 전세
        let tradePrice = parsePrice(ele.querySelector("div.price_line .price").innerText); // 141000
        let spec = ele.querySelectorAll(" div.info_area > p:nth-child(2) > span")[0]; // 확장올수리, 정상입주, 수내중학군
        spec = spec ? spec.innerText : "";



        if( "매매|전세".indexOf(tradeType) > -1){
          if (!checkMandantoryCondition(size)) {
              return;
          }

          if (!(size in result)){
            result[size] = {'매매': 0, '전세': 0, '갭': 0, '전세가율': '-', '매매층': '-', '전세층': '-', '매매갯수': 0, '전세갯수': '0' };
            dictPricePerSize[size] = {"매매": {}, "전세": {}};
          }

          if( !dictPricePerSize[size][tradeType][aptInfo.join(',')] )
          {
            dictPricePerSize[size][tradeType][aptInfo.join(',')] = [tradePrice, getFloor(floor)[0], spec, 1]
          }
          else
          {
            let beforeValue = dictPricePerSize[size][tradeType][aptInfo.join(',')];
            let newValue = [tradePrice, getFloor(floor)[0], spec ];

            dictPricePerSize[size][tradeType][aptInfo.join(',')] = tradeTypeValueFnc(tradeType, beforeValue, newValue)

          }
        }

    });


    let isGrouped = document.querySelector('#address_group2').checked

    for( let key in result){
      let sellObj = dictPricePerSize[key]['매매'];
      let liveObj = dictPricePerSize[key]['전세'];

      let sellCnt = !isGrouped ? Object.keys(sellObj).length : Object.entries(sellObj).reduce( (acc, [, item]) => (parseInt(acc) + parseInt(item[3])), 0 );
      let liveCnt = !isGrouped ? Object.keys(liveObj).length : Object.entries(liveObj).reduce( (acc, [, item]) => (parseInt(acc) + parseInt(item[3])), 0 );



      // console.log(JSON.parse(JSON.stringify(sellObj)));
      // console.log(sellCnt, liveCnt)

      for( let key in sellObj ){

        let aptObj =  sellObj[key]

        if (!checkItemCondition('매매', aptObj[1], aptObj[2])){
          delete sellObj[key]
        }
      }

      let finalSellObj = Object.entries(sellObj).sort(([, a], [, b]) => a[0] - b[0]);
      let finalLivelObj = Object.entries(liveObj).sort(([, a], [, b]) => b[0] - a[0]);

      if(finalSellObj && finalSellObj.length){
        result[key]['매매'] = finalSellObj[0][1][0];
        result[key]['매매층'] = finalSellObj[0][1][1];
      }
      result[key]['매매갯수'] = sellCnt;

      if(finalLivelObj && finalLivelObj.length){
        let idx = 0;


        result[key]['전세'] = finalLivelObj[idx][1][0];
        result[key]['전세층'] = finalLivelObj[idx][1][1];
        result[key]['전세갯수'] = liveCnt;

        let idx2 = finalLivelObj.length-1;
        result[key]['전세2'] = finalLivelObj[idx2][1][0];
        result[key]['전세층2'] = finalLivelObj[idx2][1][1];

        result[key]['갭'] = parseInt(result[key]['매매']) - parseInt(result[key]['전세']);
        result[key]['전세가율'] = parseInt( parseInt(result[key]['전세']) / parseInt(result[key]['매매']) * 100) + "%";
      }


//       console.log('finalSellObj', finalSellObj);
//       console.log(finalLivelObj);
    }



    // console.log(dictPricePerSize);
    // console.log(result)

    return result;
}

function addInfoToScreen(infos) {

    var oldScreenInfo = document.querySelector("#summaryInfo > div.complex_summary_info > div.complex_price_info");
    if (oldScreenInfo)
        oldScreenInfo.remove();

    var screenInfo = document.createElement('div');
    screenInfo.setAttribute('class', 'complex_price_info');
    screenInfo.style.marginTop = "10px";

    for (let size in infos) {

        var strTradePriceInfo = (infos[size]['매매'] ? infos[size]['매매'] + "/" + infos[size]['매매층'] : "0/-");
        var strLeasePriceInfo = (infos[size]['전세'] ? infos[size]['전세'] + "/" + infos[size]['전세층'] : "0/-");
        var strLeasePriceInfo2 = (infos[size]['전세2'] ? infos[size]['전세2'] + "/" + infos[size]['전세층2'] : "0/-");

        var additionalInfos = [];
        if (infos[size]['매매'] && infos[size]['전세']) {
            additionalInfos.push(infos[size]['갭']);
            additionalInfos.push(infos[size]['전세가율']);
        }

        if (infos[size]['매매']) {
            var py = parseInt(/\d+/g.exec(size), 10) / 3.3;
            additionalInfos.push(parseInt(infos[size]['매매'] / py));
        }

        var strAdditionalInfo = "";
        // if (additionalInfos.length > 0){

          // strAdditionalInfo += "  (" + additionalInfos.join(", ") + ")("+infos[size]['매매갯수']+"/"+infos[size]['전세갯수']+")";

          if(document.querySelector('#address_group2').checked)
            strAdditionalInfo += additionalInfos.length > 0 ? "  (" + additionalInfos.join(", ") + ")("+infos[size]['매매갯수']+"/"+infos[size]['전세갯수']+")" : "  ("+infos[size]['매매갯수']+"/"+infos[size]['전세갯수']+")";
          else
            strAdditionalInfo += additionalInfos.length > 0  ? "  (" + additionalInfos.join(", ") + ")" : "";


        // }


        var cloned = document.querySelector("#summaryInfo > div.complex_summary_info > div.complex_trade_wrap > div > dl:nth-child(1)").cloneNode(true);
        cloned.setAttribute("added", true);
        cloned.getElementsByClassName("title")[0].innerText = size;

        var trade = cloned.getElementsByClassName("data")[0];
        var lease = trade.cloneNode(true);
        var lease2 = trade.cloneNode(true);
        var additionalInfo = trade.cloneNode(true);
        var delim = trade.cloneNode(true);

        // remove, then reordering (please make it more fancy)
        trade.innerText = strTradePriceInfo;
        trade.style.color = '#f34c59';
        lease.innerText = strLeasePriceInfo;
        lease.style.color = '#4c94e8';
        lease2.innerText = strLeasePriceInfo2;
        lease2.style.color = '#4c94e8';
        delim.innerText = " / ";
        delim.style.color = '#ffffff';
        additionalInfo.innerText = strAdditionalInfo;

        // remove, then reordering (please make it fancy..)
        cloned.removeChild(trade);

        cloned.appendChild(delim);
        cloned.appendChild(trade);
        cloned.appendChild(delim.cloneNode(true));
        cloned.appendChild(lease);
        cloned.appendChild(delim.cloneNode(true));
        cloned.appendChild(lease2);
        cloned.appendChild(delim.cloneNode(true));
        cloned.appendChild(additionalInfo);

        screenInfo.appendChild(cloned);
    }

    document.querySelector("#summaryInfo > div.complex_summary_info").insertBefore(screenInfo, document.querySelector("#summaryInfo > div.complex_summary_info > div.complex_detail_link"))
}

function sortOnKeys(dict) {

  var tempDict = {};

  let sorted = jQuery('#complexOverviewList > div.list_contents > div.list_fixed > div.list_filter > div > div:nth-child(2) > div > div > ul > li label.checkbox_label')
      .map((idx, item) => {
      return item.innerText.replace('㎡', '');
    })


    let keys = Object.keys(dict)


    sorted.map( (idx, item) => {
      keys.map( (key) => {
        if( key.indexOf(item) === 0 ) tempDict[key] = dict[key]
      })
    })

    return tempDict;
}


var g_lastSelectedApt = "";

function addObserverIfDesiredNodeAvailable() {
    var target = document.getElementsByClassName('map_wrap')[0];
    var inDebounce;
    if (!target)
        return;

    //  35평이상 포함여부 check
    if(!validityCheck[LOW_JEONSE_CHECK].isCreate)   createCheckBox(LOW_JEONSE_CHECK);
    if(!validityCheck[SEANGO_CHECK].isCreate)       createCheckBox(SEANGO_CHECK);
    if(!validityCheck[AREA_CHECK].isCreate)         createCheckBox(AREA_CHECK);

    jQuery(document).on('click', (e) => {

      if( jQuery(e.target).parents('a.item_link').length > 0 )
        setTimeout((runFnc) => { jQuery('.detail_panel').css("left", "450px"); }, 500);

    });



    var observer = new MutationObserver(function(mutations) {

        mutations.forEach(function(mutation) {
            [].slice.call(mutation.addedNodes).forEach(function(addedNode) {
                //console.log('???');
                //console.log(addedNode.classList);

                if (!addedNode.classList ||
                    (!addedNode.classList.contains('infinite_scroll') && !addedNode.classList.contains('item'))) {
                    return;
                }

                if (!document.querySelector("#complexTitle")) {
                    console.log("Unexpected issues #1");
                    return;
                }

                if (document.querySelector("#complexTitle").innerText != g_lastSelectedApt) {
                    document.querySelectorAll("#summaryInfo > div.complex_summary_info > div.complex_trade_wrap > div > dl").forEach(function(ele) {
                        if (ele.hasAttribute("added"))
                            ele.remove();
                    });
                    g_lastSelectedApt = document.querySelector("#complexTitle").innerText;
                }



                //console.log('result ', result);
                document.querySelector("#complexOverviewList > div > div.item_area > div").scrollTop =
                  document.querySelector("#complexOverviewList > div > div.item_area > div").scrollHeight;

                //document.querySelector("#complexOverviewList > div > div.item_area > div").scrollTop = 0;
                 var runFnc = function (){

                    jQuery('.list_panel').css("width", "450px");
                    jQuery('.detail_panel').css("left", "450px");
                    result = getPrice_WeolbuStandard();
                    result = sortOnKeys(result);
                    addInfoToScreen(result);
                    document.querySelector(".item_list--article").scrollTop = 0;
                 }

                 if(inDebounce) clearTimeout(inDebounce)
                 inDebounce = setTimeout(runFnc, 500);


            });
        });
    });

    var config = {
        childList: true,
        subtree: true,
    };

    observer.observe(target, config);

}

addObserverIfDesiredNodeAvailable();