NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name local_RC 2ss_boulder_edit+new(id=name+date) // @license GPL-3.0-or-later; http://www.gnu.org/licenses/gpl-3.0.txt // @version 2 // @grant GM.xmlHttpRequest // @require https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js //// @match https://rc.crags.ru/spots/17/sectors/36/routes/* //// @match http://rc.crags.ru/spots/17/sectors/36/routes/* // @match http://staging.rovingclimbers.ru/spots/17/sectors/36/routes/* // @match https://staging.rovingclimbers.ru/spots/17/sectors/36/routes/* // ==/UserScript== // ==/UserScript== let SERVER;//="http://staging.rovingclimbers.ru"; const SECT_ID=36;//северная стена трудность const GS_MAX_NUMBER=300;// в северной стене не больше, чем столько трасс const KIND="boulder"; const Y_LEFT=0.946;//0.001* количество пикселей по горизонтали на схеме (на самом деле важно только отношение) const X_TOP=0.669;//0.001* количество пикселей по вертикали на схеме const MIN=1.5;//диаметр кружочка (точнее чуть больше) в процентах по горизонтали*Y_LEFT=в процентах по вертикали*X_TOP //const URL_GOOGLE_SPREADSHEET="https://spreadsheets.google.com/feeds/cells/1joxwmdCoP4wSaZvimDD0RbSpdxMNQvfanXxTA_RwOu8/1/public/values?alt=json"; //https://docs.google.com/spreadsheets/d/e/2PACX-1vTGWk7LBkpL5PCc7KS9Y3GyxtQ_nulmENbeq95DZFnB-AMbIN9WmCjc4e6rLQ2HYi_pInr8rwmFSqZW/pubhtml //https://docs.google.com/spreadsheets/d/e/2PACX-1vRJgXpN0CxEllB57VpQhuyPaxt9aj21DFUpRmgMsSPQ-L321dQihXB93h_k4igyyRTXGvvrMd9RWGBD/pubhtml //const URL_GOOGLE_SPREADSHEET_POSITION="https://spreadsheets.google.com/feeds/cells/1joxwmdCoP4wSaZvimDD0RbSpdxMNQvfanXxTA_RwOu8/3/public/values?alt=json"; const URL_GOOGLE_SPREADSHEET="https://spreadsheets.google.com/feeds/cells/1w_rwuJLGzLgZvUNH4Sh4VRThY2RzMRiyMvCVzYSdT4o/1/public/values?alt=json"; const URL_GOOGLE_SPREADSHEET_POSITION="https://spreadsheets.google.com/feeds/cells/1w_rwuJLGzLgZvUNH4Sh4VRThY2RzMRiyMvCVzYSdT4o/3/public/values?alt=json"; let URL_FOR_JSON;//=SERVER+"/api/v1/sectors/"+SECT_ID+"/routes"; let URL_FOR_REQUEST;//=SERVER+"/api/v1/routes/";//+id трассы let POSITION; let begin_find_new_route=2; //Эта функция забивает значения в переменную POSITION где расположены углы скалодрома, какой номер у трассы там и т.д. // В дальнейшем используется для вычисления координат всех трасс function set_positiondata(){ console.log("POSITION"); POSITION=[];/*=[{p:{x:73,y:11},num:1,len:5,side:-1}, //и т.д. x- координата в процентах сверху, y- координата в процентах слева, //num номер трассы, для которой задаются координаты //len количество трасс в промежутке от этой точки до следующей //side в какую сторону сдвигать сначала влево или вправо //все данные хранятся в отдельной вкладке гугл-таблицы ];*/ $.getJSON(URL_GOOGLE_SPREADSHEET_POSITION, function(values) { //console.log("данные для расположения"); let tableDataPosition={}; let data = values.feed.entry; //console.log(data.length); for (let i = 0; i < data.length; i++) {//здесь они добавляются let cell = data[i]["gs$cell"]; let row = cell.row; let col = cell.col; let val = cell["$t"]; tableDataPosition[row] = tableDataPosition[row] || {}; tableDataPosition[row][col] = val; }; //console.log(tableDataPosition); // Получить значение из ячейки (row, col) или пустую строку, если значение в ячейке не определено. function getCellValuePosition(row, col) { return (tableDataPosition[row] || {})[col] || ""; } console.log(getCellValuePosition(1,1)); for (let i = 1; getCellValuePosition(i,1)!=""; i++) POSITION.push({ p:{ x:Number(getCellValuePosition(i,1)), y:Number(getCellValuePosition(i,2)) }, v:{ x:Number(getCellValuePosition(i,4)), y:Number(getCellValuePosition(i,5)), } }) }); } function normVect(a){// длина вектора (обратите внимание, не стандартная метрика, а сжатая с разной степенью по вертикали и горизонтали) return Math.sqrt((a.x*X_TOP)*(a.x*X_TOP)+(a.y*Y_LEFT)*(a.y*Y_LEFT)); } function sumVect(a,b){//сложение векторов return {x:(a.x+b.x), y:(a.y+b.y)}; } function multNumVect(mul,a){ return {x:mul*a.x, y:mul*a.y}; } function normalizeVect(a){ return multNumVect(1/normVect(a),a); } function rotate(a){//поворот против часовой стрелки на 90 градусов let c={x:(-a.y*Y_LEFT/X_TOP),y:a.x*X_TOP/Y_LEFT}; //console.log(c); //console.log("rotated"); return c; } //Вычисляем координаты трассы. function calculate_coord_absolute(POSITION,route_num){ let num_of_sector=(route_num%100-route_num%10)/10; console.log("num_of_sector"); console.log(num_of_sector); let vector_segment=normalizeVect(POSITION[num_of_sector].v); console.log("vector_segment"); console.log(vector_segment); let n=(route_num%10000-route_num%100)/100-1; let n_straight=Math.round(n/2)*2;//каждая вторая трасса будет прямо на отрезке (соединяющем точки), а остальные сдвигаются вправо или влево. Это номер ближайшей вниз. let side=n%2;//чередует 0,1,0,1,0,1,0,1 //console.log("side "+side+" straight "+straight); let result; //let lambda=number_in_segment/total_in_segment; // let lambda=number_in_segment/N/step; // let lambda_straight=n_straight/N; result=sumVect(POSITION[num_of_sector].p,multNumVect(n*MIN/2,vector_segment)); console.log("result"); console.log(result); //let x=1/2*MIN; let y=Math.sqrt(3)/2*MIN; //let correction=sumVect(multNumVect(straight*x,vector_segment),multNumVect(straight*y,rotate(vector_segment)));//это насколько надо сдвинуться let correction=multNumVect(side*y,rotate(vector_segment));//это насколько надо сдвинуться console.log("correction"); console.log(correction); //console.log(result); result=sumVect(result,correction); console.log(result); let tr_vect=rotate(vector_segment); tr_vect=multNumVect((MIN*(route_num%10)*2),tr_vect); result=sumVect(result,tr_vect); return result; } (function (window, undefined) { // Сначала определяем скрипт запустился на страничке с трассой или при создании новой трассы let id_route; let current_url=document.URL; let b=current_url.split("/"); console.log(b); if (b[b.length-1]=="") b.splice(-1,1); if (b[b.length-1]=="new") id_route="" else if (b[b.length-1]=="edit") id_route=b[b.length-2] else id_route=b[b.length-1]; b=current_url.split(".ru/"); SERVER=b[0]+".ru"; console.log("SERVER "+SERVER); console.log("id_route "+id_route); URL_FOR_JSON=SERVER+"/api/v1/sectors/"+SECT_ID+"/routes"; URL_FOR_REQUEST=SERVER+"/api/v1/routes/"; let tableData = {};//сюда будут добавлены значения из гугл-таблицы. function theRealFunction(){ console.log(URL_FOR_JSON); //В случае удачной загрузки данных из нужной гугл-странички $.getJSON(URL_GOOGLE_SPREADSHEET, function(values) { $.getJSON(URL_FOR_JSON, function(valuesRC) { set_positiondata(); //console.log(POSITION); let lengthRC=valuesRC.payload.length; let data = values.feed.entry; for (let i = 0; i < data.length; i++) {//здесь они добавляются let cell = data[i]["gs$cell"]; let row = cell.row; let col = cell.col; let val = cell["$t"]; tableData[row] = tableData[row] || {}; tableData[row][col] = val; }; // Получить значение из ячейки (row, col) или пустую строку, если значение в ячейке не определено. function getCellValue(row, col) { return (tableData[row] || {})[col] || ""; //return 1111;//это затычка на случай проблем с гугл-таблицей } // Получить номер строки в гугл-таблице (а точнее куда это скопировано) с данным именем(номером), сектором и датой накрутки function getNumRow(name,sect,date){ let num_row = 1; //console.log(name); //console.log(getCellValue(6,2)); do { num_row++; } while ((name!=getCellValue(num_row,2)||(date!=getCellValue(num_row,11))||(sect!=getCellValue(num_row,11))) && (num_row<GS_MAX_NUMBER)); if (num_row==GS_MAX_NUMBER) { num_row=-1; console.log("Трасса в гугл-таблице с именем '"+name+" и датой накрутки"+date+"' не найдена. Такого быть не должно или она удалена."); }; return num_row; } // Получить номер строки в данных RC, с данным id function getNumRowRC(id){ var num_row_rc=-1; console.log("id"+id); console.log(valuesRC.payload[6].id); for (let num_row_rc = 0; num_row_rc < lengthRC; num_row_rc++) if (valuesRC.payload[num_row_rc].id==id) return num_row_rc; console.log("Трасса в RC не внесена, а у нее есть id. Такого быть не должно."); return -1; } // Получить номер строки в данных RC, с данным именем и датой накрутки function getNumRowRCbyName(name,date){ // var num_row_rc=-1; for (let num_row_rc = 0; num_row_rc < lengthRC; num_row_rc++) if ((valuesRC.payload[num_row_rc].name==name) && (valuesRC.payload[num_row_rc].installed_at==date)) return num_row_rc; console.log("Трасса в RC с именем "+name+" и датой накрутки "+name+" не найдена. Надо ее внести."); return -1; } // Получить id трассы в данных RC, по номеру строки function getIdRCbyNum(num_row_rc){ if (num_row_rc!=-1) return valuesRC.payload[num_row_rc].id; return -1; } // Получить id трассы в данных RC, с данным именем и датой накрутки function getIdRCbyName(name,date){ return getIdRCbyNum(getNumRowRCbyName(name,date)); } // Ищем строку в гугл-таблице, которая еще не внесена в RC function findNewNumRow(lengthRC,num_row_gs){//второе число с какой строчки начинать искать новые трассы. let new_num_row_gs=-1; //let num_row_gs=2; do { let num_row_rc; //console.log("findNewNumRow"); for (num_row_rc = 0; num_row_rc < lengthRC; num_row_rc++) { if (valuesRC.payload[num_row_rc].name==getCellValue(num_row_gs,2)){ num_row_rc = lengthRC+1; }; }; if (num_row_rc==lengthRC+2){ num_row_gs++; //alert("Совпало"); } else { console.log("findNewNumRow "+getCellValue(num_row_gs,2)+"."); console.log("findNewNumRow "+getCellValue(num_row_gs,2)+"."); if ((getCellValue(num_row_gs,2)=="")||(getCellValue(num_row_gs,13)!=""))//имя пустое или трасса скручена num_row_gs++ else new_num_row_gs=num_row_gs; }; } while ((num_row_gs<GS_MAX_NUMBER)&&(new_num_row_gs==-1)); return new_num_row_gs; } // Возвращаем объект со всей информацией, которая есть про трассу. function getInfGS(num_row,coord){ //console.log("route_inf_gs"); let route_inf_gs; //console.log(route_inf_gs); route_inf_gs={ route:{ sector_id: SECT_ID, //35 для трудности в северной стене kind:KIND, number: getCellValue(num_row,1),//Номер трассы --- мой сложный номер name: getCellValue(num_row,2),// имя трассы --- сектор точка номер category: getCellValue(num_row,3),// категория description: getCellValue(num_row,7),//описание installed_at: getCellValue(num_row,11),//накручено author_id: getCellValue(num_row,10),//id рутсеттера //1-черные,2-зеленые,3-красные,4-синие,5-фиолетовые, 6-желтые, //7-темно-розовые, 8-розовые,9-белые,10-бирюзовые, 11-оранжевые, 12-серые installed_until:getCellValue(num_row,13),//дата, когда скручена mark:{colors:{holds:getCellValue(num_row,12)}},// код цвета }, data:{position:{top: coord.x,left: coord.y}}, //координаты на схеме // } }; // alert(route_inf_gs["installed_until"]); // if (route_inf_gs["installed_until"]===undefined){ // delete route_inf_gs["installed_until"]; // } // console.log(); // if (route_inf_gs["route"]["author_id"]=="0"){ delete route_inf_gs["route"]["author_id"]; //console.log(route_inf_gs); //alert(getCellValue(num_row,20)); //route_inf_gs["route"]["description"]="Рутсеттер: "+getCellValue(num_row,20)+". "+route_inf_gs["route"]["description"];// добавляем в описание рутсеттера } console.log("route_inf_gs"); console.log(route_inf_gs); // alert(JSON.stringify(route_inf_gs)); return route_inf_gs; } // Дописывем информацию о цветах зацепок, положения стрелочек (чтобы они не стерлись при перезаписи), убираем когда скручено function correction_of_existing(route_inf_correction,num_row_rc,coord){ if (valuesRC.payload[num_row_rc]["data"]){ route_inf_correction["data"]=valuesRC.payload[num_row_rc]["data"]; route_inf_correction["data"]["position"]={top:coord.x,left:coord.y}; }; if (valuesRC.payload[num_row_rc]["mark"]){ let a=route_inf_correction["route"]["mark"]["colors"]["holds"]; route_inf_correction["route"]["mark"]=valuesRC.payload[num_row_rc]["mark"]; route_inf_correction["route"]["mark"]["colors"]["holds"]=a; }; if (route_inf_correction["route"]["installed_until"]==""){ delete route_inf_correction["route"]["installed_until"]; } return route_inf_correction; } //пересчитываем все данные по строчкам с begin по end function recalculate(begin,end){ let tempstring=""; for (let num_row=begin;num_row<=end;num_row++){ let coord=calculate_coord_absolute(POSITION,getCellValue(num_row,1)); let route_inf_correction=getInfGS(num_row,coord); let num_row_rc=getNumRowRCbyName(getCellValue(num_row,2),getCellValue(num_row,11)); let id_current_route=getIdRCbyNum(num_row_rc); if (id_current_route!=-1){ route_inf_correction=correction_of_existing(route_inf_correction,num_row_rc,coord); GM.xmlHttpRequest({ url:URL_FOR_REQUEST+id_current_route, method: 'PATCH', headers: {'Content-Type': 'application/json;charset=utf-8'}, data: JSON.stringify(route_inf_correction), onload: function(response) {console.log("GM.xmlHttpRequest response");console.log(response);} }); } } } //Создаем трассы с begin по end function setnew(begin,end){ let tempstring=""; for (let num_row=begin;num_row<=end;num_row++){ let coord=calculate_coord_absolute(POSITION,getCellValue(num_row,1)); let route_inf_correction=getInfGS(num_row,coord); delete route_inf_correction["route"]["installed_until"]; //let num_row_rc=getNumRowRCbyName(getCellValue(num_row,2),getCellValue(num_row,11)); //let id_current_route=getIdRCbyNum(num_row_rc); //if (id_current_route!=-1){ //route_inf_correction=correction_of_existing(route_inf_correction,num_row_rc,coord); GM.xmlHttpRequest({ url:URL_FOR_REQUEST, method: 'POST', headers: {'Content-Type': 'application/json;charset=utf-8'}, data: JSON.stringify(route_inf_correction), onload: function(response) {console.log("GM.xmlHttpRequest response");console.log(response);} }); //} } } // Помечаем в RC скрученными все, что отмечено скрученным в гугл-таблице function uninstall(begin,end){ // let tempstring=""; for (let num_row=begin;num_row<=end;num_row++){ if (getCellValue(num_row,13)!=""){ let id_current_route=getIdRCbyName(getCellValue(num_row,2),getCellValue(num_row,11)); if (id_current_route!=-1){ alert("Для трассы "+getCellValue(num_row,2)+" c датой накрутки "+getCellValue(num_row,11)+" с id "+id_current_route+" будет установлена дата скрутки "+getCellValue(num_row,13)); GM.xmlHttpRequest({ url:URL_FOR_REQUEST+id_current_route, method: 'PATCH', headers: {'Content-Type': 'application/json;charset=utf-8'}, data: JSON.stringify({route:{installed_until:getCellValue(num_row,13)}}), onload: function(response) { //console.log("GM.xmlHttpRequest response");console.log(response); } }); // tempstring=tempstring+"fetch('"+URL_FOR_REQUEST+id_current_route+"', {method: 'PATCH',headers: {'Content-Type': 'application/json;charset=utf-8'}, body: '"; // tempstring=tempstring+JSON.stringify({route:{installed_until:getCellValue(num_row,13)}})+"'});\n"; } } } // console.log(tempstring); } //помечаем скрученными трассы, которые отсутствуют в гугл-таблице (в дату, которая аргумент) function correct(date){ for (num_row_rc = 0; num_row_rc < lengthRC; num_row_rc++) if (getNumRow(valuesRC.payload[num_row_rc].name,valuesRC.payload[num_row_rc].installed_at)==-1){ alert("Нет в гугл-таблице сведений о трассе "+JSON.stringify(valuesRC.payload[num_row_rc]) +". Если нажмете ок, то пометим, что она скручена в дату "+date+"."); GM.xmlHttpRequest({ url:URL_FOR_REQUEST+valuesRC.payload[num_row_rc].id, method: 'PATCH', headers: {'Content-Type': 'application/json;charset=utf-8'}, data: JSON.stringify({route:{installed_until:date}}), onload: function(response) {console.log("GM.xmlHttpRequest response");console.log(response);} }); } alert("В гугл таблице есть сведения обо всех трассах в RC."); } //Добавляем информацию в зависимости от того, что написано в строчке. //Если ничего, то обновляем информацию о трассе (или если это добавление трассы, то добавляем новую) function addGS(newDiv,newInput){ if (newInput.value!=""){ let a=newInput.value.split("/"); if (a[0]=="r") recalculate(Number(a[1]),Number(a[2])); if (a[0]=="u") if (a.length==1) uninstall(2,300) else uninstall(Number(a[1]),Number(a[2])); if (a[0]=="c") correct((a[1])); if (a[0]=="n") setnew(Number(a[1]),Number(a[2])); } else{ let num_row_rc; let num_row; if (id_route==""){ num_row=findNewNumRow(lengthRC,begin_find_new_route); console.log(num_row); } else{ console.log('"'+id_route+'"'); console.log(getNumRowRC(id_route)); num_row_rc=getNumRowRC(id_route); num_row=getNumRow(valuesRC.payload[num_row_rc].name,valuesRC.payload[num_row_rc].installed_at); }; console.log(begin_find_new_route); console.log(num_row+" num_row"); console.log(newDiv.innerHTML+(newDiv.innerHTML=="")); if ((newDiv.innerHTML=="")||(newDiv.innerHTML=="Новая трасса добавлена. Но это не точно.")||(newDiv.innerHTML=="Новых трасс нет.")){ if (num_row==-1) { newDiv.innerHTML="Новых трасс нет."; } else{ if (id_route==""){ newDiv.innerHTML="Новая трасса в гугл таблице "+getCellValue(num_row,2)+" в строке "+num_row+"."; } else newDiv.innerHTML=""; let coord=calculate_coord_absolute(POSITION,getCellValue(num_row,1)); for (let i = 1; i < 8; i++) newDiv.innerHTML = newDiv.innerHTML+" "+getCellValue(num_row,i); newDiv.innerHTML = newDiv.innerHTML+". top:"+coord.x+", left:"+coord.y+". Для того, чтобы загрузить данные, нажмите еще раз на кнопку."; } } else { let coord=calculate_coord_absolute(POSITION,getCellValue(num_row,1)); let route_inf_correction = getInfGS(num_row,coord); if (id_route!=""){ console.log("route_inf_correction:"); console.log(route_inf_correction); console.log("2 route_inf_correction:"); console.log(route_inf_correction); console.log(num_row_rc); route_inf_correction=correction_of_existing(route_inf_correction,num_row_rc,coord); console.log("route_inf_correction:"); console.log(route_inf_correction); let aa=JSON.stringify(route_inf_correction); console.log(aa+" "+URL_FOR_REQUEST+id_route); GM.xmlHttpRequest({ url:URL_FOR_REQUEST+id_route, method: 'PATCH', headers: {'Content-Type': 'application/json;charset=utf-8'}, data: JSON.stringify(route_inf_correction), onload: function(response) {console.log("GM.xmlHttpRequest response");console.log(response);} }); } else { console.log(route_inf_correction); console.log(route_inf_correction); if (route_inf_correction.name!=""){ console.log(getNumRowRCbyName(route_inf_correction.route.name,route_inf_correction.route.installed_at)); if (getNumRowRCbyName(route_inf_correction.route.name,route_inf_correction.route.installed_at)==-1){ let aa=JSON.stringify(route_inf_correction); console.log(aa+" NEW "+num_row); begin_find_new_route=num_row+1; GM.xmlHttpRequest({ url:URL_FOR_REQUEST, method: 'POST', headers: {'Content-Type': 'application/json;charset=utf-8'}, data: JSON.stringify(route_inf_correction), onload: function(response) {console.log("GM.xmlHttpRequest response");console.log(response);} }); console.log(route_inf_correction); newDiv.innerHTML="Новая трасса добавлена. Но это не точно."; } else alert("ТРАССА С ТАКИМ ИМЕНЕМ И ДАТОЙ НАКРУТКИ УЖЕ ЕСТЬ."); } else alert("ПЫТАЮТСЯ ВНЕСТИ ТРАССУ С ПУСТЫМ ИМЕНЕМ."); }; //*/ }; }; } // Добавление кнопки с добавлением данных function addGSBtn(ele,num_child) { let newDiv = document.createElement("div"); let btn = document.createElement("button"); let newInput = document.createElement("input"); ele.appendChild(btn); newDiv.innerHTML = ""; let newInput_result=ele.appendChild(newInput); let newDiv_result=ele.appendChild(newDiv); //btn.innerHTML = "Добавить данные из таблицы"; btn.innerHTML = `При нажатии на кнопку загрузятся данные из гугл таблицы и вычислятся координаты. Если ничего не писать, то обновятся данные о трассе (если это просмотр трассы) но после второго нажатия на кнопку. Или добавится новая трасса (если это добавление трассы) после второго нажатия на кнопку, но там будет понятно. И можно много трасс добавлять. ('u'-скрутит скрученные трассы) \n ('u/2/13'- скрутит скрученные трассы с 2 по 13 строчки)\n ('r/2/13'- пересчитает все данные с 2 по 13 строчки)\n ('c/21-05-2021' пометит все отстутствующие в гугл-таблице трассы скрученными в эту дату)`; btn.className = "GBtn"; //btn.onclick=() => {alert(1);}; btn.onclick=() =>{addGS(newDiv_result,newInput_result);}; //btn.onclick=() =>{recalculate(2,185);}; //console.log(ele.childNodes[num_child+1]); } var num_div_cat; //console.log("jhsdbcjjbds"); var elements_div=document.getElementsByTagName("div"); for (let i = 0; i < elements_div.length; i++){ if (elements_div[i].innerText){ //console.log(elements_div[i].innerText+" "+i); if ((elements_div[i]["innerText"]=="Сложность:")||elements_div[i]["innerText"]=="Категория:"){ num_div_cat=i; //console.log(i) } } }; //console.log(num_div_cat); //td = routeN[2].parentNode; //addGSBtn(td,5); td = elements_div[num_div_cat]; addGSBtn(td,0); //alert(getCellValue(4,5)); }); }); } setTimeout(theRealFunction, 5000); //Кстати, это уменьшает количество выдаваемых ошибок, так как страничка дозагружается за 10 секунд. })(window); //})();